支持新的 Android 插件 API
如果您不编写或维护 Android Flutter 插件,可以跳过此页面。
从 1.12 版本开始,Android 平台提供了新的插件 API。基于 PluginRegistry.Registrar
的旧 API 不会立即弃用,但我们鼓励您迁移到基于 FlutterPlugin
的新 API。
与旧 API 相比,新 API 的优势在于为依赖于生命周期的组件提供了一组更清晰的访问器。例如,如果 Flutter 未连接到任何 Activity,PluginRegistry.Registrar.activity()
可能会返回 null。
换句话说,使用旧 API 的插件在将 Flutter 嵌入 Android 应用时可能会产生未定义行为。由 flutter.dev 团队提供的 Flutter 插件 大部分已经迁移。 (了解如何成为 pub.dev 上的 认证发布者!)有关使用新 API 的插件示例,请参阅 battery plus 包。
升级步骤
#以下说明概述了支持新 API 的步骤
更新主插件类(
*Plugin.java
)以实现FlutterPlugin
接口。对于更复杂的插件,您可以将FlutterPlugin
和MethodCallHandler
分离到两个类中。有关使用最新版本(v2)嵌入访问应用资源的更多详细信息,请参阅下一节 基础插件。
另外,请注意,插件仍应包含静态registerWith()
方法,以保持与不使用 v2 Android 嵌入的应用兼容。(有关详细信息,请参阅 升级 1.12 之前的 Android 项目。)最简单的方法(如果可能)是将registerWith()
中的逻辑移到一个私有方法中,该方法可以由registerWith()
和onAttachedToEngine()
调用。只会调用registerWith()
或onAttachedToEngine()
中的一个,不会同时调用。
此外,您应该记录插件中所有未重写的公共成员。在“添加到应用”场景中,开发者可以访问这些类,因此需要进行文档记录。(可选)如果您的插件需要
Activity
引用,还要实现ActivityAware
接口。(可选)如果您的插件预计在任何时候都驻留在后台服务中,请实现
ServiceAware
接口。更新示例应用的
MainActivity.java
以使用 v2 嵌入的FlutterActivity
。有关详细信息,请参阅 升级 1.12 之前的 Android 项目。您可能需要为您的插件类创建一个公共构造函数(如果之前不存在)。例如:MainActivity.javajavapackage 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. }
(可选)如果您删除了
MainActivity.java
,请更新<plugin_name>/example/android/app/src/main/AndroidManifest.xml
以使用io.flutter.embedding.android.FlutterActivity
。例如:AndroidManifest.xmlxml<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>
(可选)创建一个
EmbeddingV1Activity.java
文件,该文件在MainActivity
相同的文件夹中使用 v1 嵌入,用于示例项目,以继续测试 v1 嵌入与您的插件的兼容性。请注意,您必须手动注册所有插件,而不是使用GeneratedPluginRegistrant
。例如:EmbeddingV1Activity.javajavapackage 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")); } }
在
<plugin_name>/example/android/app/src/main/AndroidManifest.xml
中添加<meta-data android:name="flutterEmbedding" android:value="2"/>
。这将设置示例应用使用 v2 嵌入。(可选)如果您在上一步中创建了
EmbeddingV1Activity
,请将EmbeddingV1Activity
添加到<plugin_name>/example/android/app/src/main/AndroidManifest.xml
文件中。例如:AndroidManifest.xmlxml<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>
测试您的插件
#接下来的步骤将介绍测试您的插件,我们鼓励您这样做,但并非必需。
更新
<plugin_name>/example/android/app/build.gradle
以将对android.support.test
的引用替换为androidx.test
build.gradlegroovydefaultConfig { ... testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" ... }
build.gradlegroovydependencies { ... androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:rules:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' ... }
在
<plugin_name>/example/android/app/src/androidTest/java/<plugin_path>/
中为MainActivity
和EmbeddingV1Activity
添加测试文件。您需要创建这些目录。例如:MainActivityTest.javajavapackage 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.javajavapackage 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); }
将
integration_test
和flutter_driver
dev_dependencies 添加到<plugin_name>/pubspec.yaml
和<plugin_name>/example/pubspec.yaml
中。pubspec.yamlyamlintegration_test: sdk: flutter flutter_driver: sdk: flutter
更新
<plugin_name>/pubspec.yaml
中的最低 Flutter 版本要求。所有未来的插件都将设置最低版本为 1.12.13+hotfix.6,这是我们可以保证支持的最低版本。例如:pubspec.yamlyamlenvironment: sdk: ">=2.16.1 <3.0.0" flutter: ">=1.17.0"
在
<plugin_name>/test/<plugin_name>_test.dart
中创建一个简单的测试。为了测试添加 v2 嵌入支持的 PR,我们试图测试插件的一些非常基本的功能。这是一个冒烟测试,以确保插件能够正确地与新的嵌入器进行注册。例如:dartimport '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); }); }
本地运行
integration_test
测试。在终端中,执行以下操作:flutter test integration_test/app_test.dart
基础插件
#要开始使用代码中的 Flutter Android 插件,请先实现 FlutterPlugin
。
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
,提供对DartExecutor
、FlutterRenderer
等组件的访问。 - binding.getApplicationContext()
- 返回正在运行的应用的 Android 应用的
Context
。
UI/Activity 插件
#如果您的插件需要与 UI 交互,例如请求权限或修改 Android UI chrome,那么您需要采取额外的步骤来定义您的插件。您必须实现 ActivityAware
接口。
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 配置。