支持新的 Android 插件 API
如果您不编写或维护 Android Flutter 插件,则可以跳过此页面。
自 1.12 版本发布以来,Android 平台提供了新的插件 API。基于 PluginRegistry.Registrar
的旧 API 不会立即弃用,但我们鼓励您迁移到基于 FlutterPlugin
的新 API。
与旧 API 相比,新 API 的优势在于为生命周期依赖组件提供了一组更清晰的访问器。例如,如果 Flutter 未附加到任何活动,PluginRegistry.Registrar.activity()
可能会返回 null。
换句话说,当将 Flutter 嵌入到 Android 应用中时,使用旧 API 的插件可能会产生未定义的行为。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
接口。(可选) 如果您的插件在任何时候都预期会保留在后台 Service 中,请实现
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>
(可选) 在与
MainActivity
相同的文件夹中为示例项目创建一个使用 v1 嵌入方式的EmbeddingV1Activity.java
文件,以继续测试 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")); } }
将
<meta-data android:name="flutterEmbedding" android:value="2"/>
添加到<plugin_name>/example/android/app/src/main/AndroidManifest.xml
。这会将示例应用设置为使用 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
开发依赖项添加到<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 界面,则需要采取额外步骤来定义您的插件。您必须实现 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 配置。