Migrating your plugin to the new Android APIs
翻译自 https://flutter.dev/docs/development/packages-and-plugins/plugin-api-migration
如果你不写或维护一个 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(): 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 到
/example/android/app/src/main/AndroidManifest.xml.
例如
android:name=".EmbeddingV1Activity"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
为了使插件在分支 master 和稳定版上支持 Flutter, 将这个 gradle 脚本添加到 /android/build.gradle。
// TODO(): 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"
}
}
}
}
测试插件
剩下的步骤涉及测试您的插件,我们鼓励,但不是必需的。
更新 /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'
...
}
在 /example/android/app/src/androidTest/java// 中为 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 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 rule =
new ActivityTestRule<>(EmbeddingV1Activity.class);
}
添加 e2e 和 flutter_driver dev_dependencies 到
/pubspec.yaml 和
/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());
}
}
在 /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"
在 /test/_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 /example
flutter build apk
cd android
./gradlew app:connectedAndroidTest -Ptarget=`pwd`/../../test/_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