支持新的 Android 的 API
如果你并非亲自开发或维护一个 Flutter 的 Android 插件,你可以跳过本页面。
自 1.12 版本发布后,
Android 平台已可以使用新的 Android 插件 API 。基于 PluginRegistry.Registrar
的 API 不会立刻废弃,但我们鼓励你向基于 FlutterPlugin
的 API 进行迁移。
相较旧的 API 而言,新版 API 的优点是为生命周期的相关组件提供了更简洁清晰的访问方式。例如,在使用旧的 PluginRegistry.Registrar.activity()
时,如果 Flutter 尚未附加到任何 activites,可能会返回 null 。
换句话说,在使用旧的 API 进行 Flutter 嵌入 Android 应用时,可能会产生意外的行为。 Flutter 开发团队提供的大部分 Flutter 插件 已经完成了迁移。(了解如何成为 认证的发布者)作为参考, battery plus package 已经迁移到新版 API 。
升级步骤
#以下的步骤简要说明了如何支持新版 API :
-
在插件的主类文件中 (
*Plugin.java
) 实现FlutterPlugin
接口。对于稍微复杂的插件,你可以将FlutterPlugin
与MethodCallHandler
拆分到不同的类中。如需更多关于如何使用新版 API 获取资源的内容,请参考下一节 基础插件 。
同时需要注意的是,插件仍需保留静态的registerWith()
方法,从而适配不兼容 v2 版本嵌入的应用。 (查看 Upgrading pre 1.12 Android projects 获取更多信息)
此外,所有不可覆盖的公开成员都应该使用文档标注。在嵌入开发的场景下,这些可见内容通常需要包含文档。 -
(可选)如果你的插件需要
Activity
的引用,请同时实现ActivityAware
接口。 -
(可选)如果你的插件需要随时保持一个后台 Service ,请实现
ServiceAware
接口。 -
使用
FlutterActivity
将示例应用中的MainActivity.java
迁移到 v2 版本嵌入。更多信息请查看 Upgrading pre 1.12 Android projects 。如果你的插件类尚不存在,则必须添加一个公有构造函数。例如: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.java
同级目录下创建一个EmbeddingV1Activity.java
文件,使用 v1 版本嵌入以持续测试你的项目对 v1 版本嵌入的兼容性。例如: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); }
-
在
<plugin_name>/pubspec.yaml
和<plugin_name>/example/pubspec.yaml
中的 dev_dependencies 下添加e2e
和flutter_driver
。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>_e2e.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); }); }
-
本地运行 e2e 测试。在终端中执行以下内容:
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()
返回当前运行的安卓应用的 Application 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 无关的配置。
除非另有说明,本文档之所提及适用于 Flutter 的最新稳定版本,本页面最后更新时间: 2024-09-04。 查看文档源码 或者 为本页面内容提出建议。