跳至主要内容

支持新的 Android 插件 API

如果你不编写或维护 Android Flutter 插件,则可以跳过此页面。

从 1.12 版本开始,Android 平台提供了新的插件 API。基于 PluginRegistry.Registrar 的旧版 API 不会立即弃用,但我们鼓励你迁移到基于 FlutterPlugin 的新版 API。

与旧版 API 相比,新版 API 的优势在于提供了一组更清晰的生命周期相关组件访问器。例如,PluginRegistry.Registrar.activity() 在 Flutter 未附加到任何 Activity 时可能会返回 null。

换句话说,使用旧版 API 的插件在将 Flutter 嵌入到 Android 应用时可能会产生未定义的行为。flutter.dev 团队提供的大多数 Flutter 插件 已经迁移了。(了解如何在 pub.dev 上成为 已验证的发行商!)有关使用新版 API 的插件示例,请参阅 battery plus 软件包

升级步骤

#

以下说明支持新版 API 的步骤

  1. 更新主插件类(*Plugin.java)以实现 FlutterPlugin 接口。对于更复杂的插件,你可以将 FlutterPluginMethodCallHandler 分离到两个类中。有关使用最新版本 (v2) 嵌入访问应用资源的更多详细信息,请参阅下一节 基础插件

    此外,请注意,插件仍应包含静态 registerWith() 方法以保持与不使用 v2 Android 嵌入的应用的兼容性。(有关详细信息,请参阅 升级 1.12 之前的 Android 项目)。最简单的方法(如果可能)是将 registerWith() 中的逻辑移动到一个私有方法中,registerWith()onAttachedToEngine() 都可以调用该方法。registerWith()onAttachedToEngine() 将会被调用,但不会同时调用两者。

    此外,你应该记录插件中所有未重写公有成员。在添加到应用的场景中,这些类对开发人员是可访问的,需要文档。

  2. (可选)如果你的插件需要 Activity 引用,则还需要实现 ActivityAware 接口。

  3. (可选)如果你的插件预计在任何时间点都将保存在后台服务中,则需要实现 ServiceAware 接口。

  4. 更新示例应用的 MainActivity.java 以使用 v2 嵌入 FlutterActivity。有关详细信息,请参阅 升级 1.12 之前的 Android 项目。如果你的插件类还没有公有构造函数,你可能需要创建一个。例如

    MainActivity.java
    java
     package io.flutter.plugins.firebasecoreexample;
    
     import io.flutter.embedding.android.FlutterActivity;
     import io.flutter.embedding.engine.FlutterEngine;
     import io.flutter.plugins.firebase.core.FirebaseCorePlugin;
    
     public class MainActivity extends FlutterActivity {
       // You can keep this empty class or remove it. Plugins on the new embedding
       // now automatically registers plugins.
     }
  5. (可选)如果你删除了 MainActivity.java,请更新 <plugin_name>/example/android/app/src/main/AndroidManifest.xml 以使用 io.flutter.embedding.android.FlutterActivity。例如

    AndroidManifest.xml
    xml
     <activity android:name="io.flutter.embedding.android.FlutterActivity"
            android:theme="@style/LaunchTheme"
    android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale"
            android:hardwareAccelerated="true"
            android:exported="true"
            android:windowSoftInputMode="adjustResize">
            <meta-data
                android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
                android:value="true" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
  6. (可选)在与 MainActivity 相同的文件夹中创建 EmbeddingV1Activity.java 文件,该文件使用 v1 嵌入来为示例项目进行测试,以保持与你的插件的 v1 嵌入的兼容性。请注意,你必须手动注册所有插件,而不是使用 GeneratedPluginRegistrant。例如

    EmbeddingV1Activity.java
    java
    package io.flutter.plugins.batteryexample;
    
    import android.os.Bundle;
    import io.flutter.app.FlutterActivity;
    import io.flutter.plugins.battery.BatteryPlugin;
    
    public class EmbeddingV1Activity extends FlutterActivity {
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        BatteryPlugin.registerWith(registrarFor("io.flutter.plugins.battery.BatteryPlugin"));
      }
    }
  7. <meta-data android:name="flutterEmbedding" android:value="2"/> 添加到 <plugin_name>/example/android/app/src/main/AndroidManifest.xml。这将设置示例应用以使用 v2 嵌入。

  8. (可选)如果你在之前的步骤中创建了 EmbeddingV1Activity,请将 EmbeddingV1Activity 添加到 <plugin_name>/example/android/app/src/main/AndroidManifest.xml 文件中。例如

    AndroidManifest.xml
    xml
    <activity
        android:name=".EmbeddingV1Activity"
        android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale"
        android:hardwareAccelerated="true"
        android:exported="true"
        android:windowSoftInputMode="adjustResize">
    </activity>

测试你的插件

#

其余步骤将介绍测试你的插件,我们鼓励你进行测试,但不是必需的。

  1. 更新 <plugin_name>/example/android/app/build.gradle 以将对 android.support.test 的引用替换为 androidx.test

    build.gradle
    groovy
    defaultConfig {
      ...
      testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
      ...
    }
    build.gradle
    groovy
    dependencies {
    ...
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test:rules:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    ...
    }
  2. <plugin_name>/example/android/app/src/androidTest/java/<plugin_path>/ 中添加 MainActivityEmbeddingV1Activity 的测试文件。你需要创建这些目录。例如

    MainActivityTest.java
    java
    package io.flutter.plugins.firebase.core;
    
    import androidx.test.rule.ActivityTestRule;
    import io.flutter.plugins.firebasecoreexample.MainActivity;
    import org.junit.Rule;
    import org.junit.runner.RunWith;
    
    @RunWith(FlutterRunner.class)
    public class MainActivityTest {
      // Replace `MainActivity` with `io.flutter.embedding.android.FlutterActivity` if you removed `MainActivity`.
      @Rule public ActivityTestRule<MainActivity> rule = new ActivityTestRule<>(MainActivity.class);
    }
    EmbeddingV1ActivityTest.java
    java
    package io.flutter.plugins.firebase.core;
    
    import androidx.test.rule.ActivityTestRule;
    import io.flutter.plugins.firebasecoreexample.EmbeddingV1Activity;
    import org.junit.Rule;
    import org.junit.runner.RunWith;
    
    @RunWith(FlutterRunner.class)
    public class EmbeddingV1ActivityTest {
      @Rule
      public ActivityTestRule<EmbeddingV1Activity> rule =
          new ActivityTestRule<>(EmbeddingV1Activity.class);
    }
  3. integration_testflutter_driver dev_dependencies 添加到 <plugin_name>/pubspec.yaml<plugin_name>/example/pubspec.yaml

    pubspec.yaml
    yaml
    integration_test:
      sdk: flutter
    flutter_driver:
      sdk: flutter
  4. 更新 <plugin_name>/pubspec.yaml 中的环境的最低 Flutter 版本。所有未来的插件都将最低版本设置为 1.12.13+hotfix.6,这是我们能够保证支持的最低版本。例如

    pubspec.yaml
    yaml
    environment:
      sdk: ">=2.16.1 <3.0.0"
      flutter: ">=1.17.0"
  5. <plugin_name>/test/<plugin_name>_test.dart 中创建一个简单的测试。为了测试添加 v2 嵌入支持的 PR,我们试图测试插件的一些非常基本的功能。这是一个冒烟测试,以确保插件正确地与新的嵌入器注册。例如

    dart
    import 'package:flutter_test/flutter_test.dart';
    import 'package:integration_test/integration_test.dart';
    
    void main() {
      IntegrationTestWidgetsFlutterBinding.ensureInitialized();
    
      testWidgets('Can get battery level', (tester) async {
        final Battery battery = Battery();
        final int batteryLevel = await battery.batteryLevel;
        expect(batteryLevel, isNotNull);
      });
    }
  6. 在本地运行 integration_test 测试。在终端中,执行以下操作

    flutter test integration_test/app_test.dart

基础插件

#

要开始使用代码中的 Flutter Android 插件,请先实现 FlutterPlugin

java
public class MyPlugin implements FlutterPlugin {
  @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
    // TODO: your plugin is now attached to a Flutter experience.
  }

  @Override
  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
    // TODO: your plugin is no longer attached to a Flutter experience.
  }
}

如上所示,你的插件可能(也可能不)在任何给定时间点与给定的 Flutter 体验相关联。你应该注意在 onAttachedToEngine() 中初始化插件的行为,然后在 onDetachedFromEngine() 中清理插件的引用。

FlutterPluginBinding 为你的插件提供了一些重要的引用

binding.getFlutterEngine()
返回你的插件附加到的 FlutterEngine,提供对 DartExecutorFlutterRenderer 等组件的访问。
binding.getApplicationContext()
返回正在运行的应用的 Android 应用的 Context

UI/Activity 插件

#

如果你的插件需要与 UI 交互,例如请求权限或更改 Android UI 界面,则需要采取其他步骤来定义你的插件。你必须实现 ActivityAware 接口。

java
public class MyPlugin implements FlutterPlugin, ActivityAware {
  //...normal plugin behavior is hidden...

  @Override
  public void onAttachedToActivity(ActivityPluginBinding activityPluginBinding) {
    // TODO: your plugin is now attached to an Activity
  }

  @Override
  public void onDetachedFromActivityForConfigChanges() {
    // TODO: the Activity your plugin was attached to was
    // destroyed to change configuration.
    // This call will be followed by onReattachedToActivityForConfigChanges().
  }

  @Override
  public void onReattachedToActivityForConfigChanges(ActivityPluginBinding activityPluginBinding) {
    // TODO: your plugin is now attached to a new Activity
    // after a configuration change.
  }

  @Override
  public void onDetachedFromActivity() {
    // TODO: your plugin is no longer associated with an Activity.
    // Clean up references.
  }
}

要与 Activity 交互,你的 ActivityAware 插件必须在 4 个阶段实现适当的行为。首先,你的插件附加到一个 Activity。你可以通过提供的 ActivityPluginBinding 访问该 Activity 及其许多回调。

由于 Activity 可以在配置更改期间被销毁,因此你必须在 onDetachedFromActivityForConfigChanges() 中清理对给定 Activity 的任何引用,然后在 onReattachedToActivityForConfigChanges() 中重新建立这些引用。

最后,在 onDetachedFromActivity() 中,你的插件应该清理与 Activity 行为相关的所有引用,并返回到非 UI 配置。