Migrating your plugin to the new Android APIs
如果你不写或维护一个 Flutter 插件,您可以跳过这一页。
从 1.10.17 版本开始,新的插件 API 在 master 和 dev 通道上可用。 不会立即淘汰旧的 API,但我们建议您迁移到新的 API。 随着时间的流逝,将 Flutter 嵌入到 Android 应用中时,使用旧 API 的插件可能会产生奇怪的行为。 flutter.dev 提供的大多数 Flutter 插件已经被迁移。 (了解如何成为 pub.dev 的 verified publisher!)有关使用新 API 的插件的示例,请参阅 battery package。
迁移步骤
以下说明概述了支持新 API 的步骤:
-
更新主插件类(* Plugin.java)以实现
FlutterPlugin[]。 对于更复杂的插件,您可以将 将FlutterPlugin和MethodCallHandler分为两个类。 见下 Basic plugin [] 部分,以获取有关通过以下方式访问应用程序资源的更多详细信息: embedding 的最新版本(v2)。
另外,请注意插件仍应包含静态的registerWith()方法, 与不使用 v2 embedding 的应用程序保持兼容。 最简单的操作(如果可能的话)是将逻辑从registerWith()中移出 进入一个私有方法,使registerWith()和onAttachedToEngine都可以调用该方法。
registerWith()或AttachToEngine()只有一个会被调用。
如果你在onAttachToEngine()中创建 channels, 那么没必要在onDetachFromEngine()中清理这些创建,当onAttachToEngine()第二次被调用时再次创建他们是可以的。
此外,您还应该在文档中记录所有 non-overridden 的公共成员。 在 add-to-app 场景中, these classes will be accessible to a developer and require documentation. -
(可选) 如果插件需要
Activity的引用,则实现ActivityAware. -
(可选) 如果您的插件预计将保存在后台服务,实现
ServiceAware. -
更新 example app 的
MainActivity.java来使用 v2 embedding 的 FlutterActivity。您可能需要一个 plugin 的公共构造函数(如果没有),例如:
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 {
// TODO(<github-username>): Remove this once v2 of
// GeneratedPluginRegistrant rolls to stable.
// https://github.com/flutter/flutter/issues/42694
@Override
public void configureFlutterEngine(FlutterEngine flutterEngine) {
flutterEngine.getPlugins().add(new FirebaseCorePlugin());
}
}
- (可选) 使用 ShimPluginRegistry 还不支持 v2 embedding 的插件。例如:
ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine);
PathProviderPlugin.registerWith(
shimPluginRegistry.registrarFor("io.flutter.plugins.pathprovider.PathProviderPlugin"));
VideoPlayerPlugin.registerWith(
shimPluginRegistry.registrarFor("io.flutter.plugins.videoplayer.VideoPlayerPlugin"));
- 在
MainActivity同文件夹下创建EmbeddingV1Activity.java文件使用 v1 embedding。例如:
package io.flutter.plugins.firebasecoreexample;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class EmbeddingV1Activity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
}
- 添加
EmbeddingV1Activity到 <plugin_name>/example/android/app/src/main/AndroidManifest.xml. 例如
<activity
android:name=".EmbeddingV1Activity"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
</activity>
- 为了使插件在分支 master 和稳定版上支持 Flutter, 将这个 gradle 脚本添加到 <plugin_name>/android/build.gradle。
// TODO(<github-username>): Remove this hack once androidx
// lifecycle is included on stable.
// https://github.com/flutter/flutter/issues/42348
afterEvaluate {
def containsEmbeddingDependencies = false
for (def configuration : configurations.all) {
for (def dependency : configuration.dependencies) {
if (dependency.group == 'io.flutter' &&
dependency.name.startsWith('flutter_embedding') &&
dependency.isTransitive())
{
containsEmbeddingDependencies = true
break
}
}
}
if (!containsEmbeddingDependencies) {
android {
dependencies {
def lifecycle_version = "1.1.1"
compileOnly "android.arch.lifecycle:runtime:$lifecycle_version"
compileOnly "android.arch.lifecycle:common:$lifecycle_version"
compileOnly "android.arch.lifecycle:common-java8:$lifecycle_version"
}
}
}
}
测试插件
剩下的步骤涉及测试您的插件,我们鼓励,但不是必需的。
- 更新
<plugin_name>/example/android/app/build.gradle将android.support.test替换为androidx.test:
defaultConfig {
...
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
...
}
dependencies {
...
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添加测试文件。例如:
package io.flutter.plugins.firebase.core;
import androidx.test.rule.ActivityTestRule;
import dev.flutter.plugins.e2e.FlutterRunner;
import io.flutter.plugins.firebasecoreexample.MainActivity;
import org.junit.Rule;
import org.junit.runner.RunWith;
@RunWith(FlutterRunner.class)
public class MainActivityTest {
@Rule public ActivityTestRule<MainActivity> rule = new ActivityTestRule<>(MainActivity.class);
}
package io.flutter.plugins.firebase.core;
import androidx.test.rule.ActivityTestRule;
import dev.flutter.plugins.e2e.FlutterRunner;
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);
}
- 添加
e2e和flutter_driverdev_dependencies 到<plugin_name>/pubspec.yaml和<plugin_name>/example/pubspec.yaml.
e2e: ^0.2.1
flutter_driver:
sdk: flutter
- 在
MainActivity.java中手动注册 E2E 插件 以及示例应用程序使用的任何其他插件。
package io.flutter.plugins.packageinfoexample;
import dev.flutter.plugins.e2e.E2EPlugin;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugins.packageinfo.PackageInfoPlugin;
public class MainActivity extends FlutterActivity {
// TODO(jackson): Remove this once v2 of GeneratedPluginRegistrant
// rolls to stable.
// https://github.com/flutter/flutter/issues/42694
@Override
public void configureFlutterEngine(FlutterEngine flutterEngine) {
flutterEngine.getPlugins().add(new PackageInfoPlugin());
flutterEngine.getPlugins().add(new E2EPlugin());
}
}
- 在
<plugin_name> /pubspec.yaml更新环境中的最低 Flutter 版本 所有插件都在目前 forward 会将最低版本设置为 1.9.1 + hotfix.4,这是我们可以保证支持的最低版本。例如:
environment:
sdk: ">=2.0.0-dev.28.0 <3.0.0"
flutter: ">=1.9.1+hotfix.4 <2.0.0"
- 在
<plugin_name>/test/<plugin_name>_e2e.dart创建一个简单测试。 为了测试添加了 v2 嵌入支持的 PR,我们正在尝试测试该插件的一些非常基本的功能。这是一个冒烟测试,以确保插件正确注册新的嵌入器。 例如:
import 'package:flutter_test/flutter_test.dart';
import 'package:battery/battery.dart';
import 'package:e2e/e2e.dart';
void main() {
E2EWidgetsFlutterBinding.ensureInitialized();
testWidgets('Can get battery level', (WidgetTester tester) async {
final Battery battery = Battery();
final int batteryLevel = await battery.batteryLevel;
expect(batteryLevel, isNotNull);
});
}
- 在本地测试运行 e2e 测试,在 terminal 执行下列操作:
cd <plugin_name>/example
flutter build apk
cd android
./gradlew app:connectedAndroidTest -Ptarget=`pwd`/../../test/<plugin_name>_e2e.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()
: Returns the FlutterEngine that your plugin is attached to,
providing access to components like the DartExecutor,
FlutterRenderer, and more.
binding.getApplicationContext() : Returns the Android application's Context for the running app.
binding.getLifecycle()
: Returns a reference that can be used to obtain a Lifecycle object.
If you need to use this lifecycle reference then you need add a
project dependency on Flutter's Android lifecycle package.
UI/Activity 插件
如果您的插件需要与用户界面进行交互, 例如请求权限或更改 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 和一堆 callbacks.
由于可以在配置更改期间销毁 Activity,
在 onDetachedFromActivityForConfigChanges() 您必须清除对指定给定 Activity 的所有引用,
然后在 onReattachedToActivityForConfigChanges() 中重建这些引用。
最后,在onDetachedFromActivity() 中,您的插件应清理
与 Activity 行为相关的所有参考,然后返回
非 UI 配置。
Service plugin
TODO
ContentProvider plugin
TODO