原文链接:Supporting the new Android plugins APIs
随着1.12版本的发布,插件API也发生了变更。旧的API基于PluginRegistry.Registrar
,这种方式不会立即被废弃,但是我们仍然鼓励开发者尽量使用基于FlutterPlugin
的新的集成方式。
和旧的API相比,新的API更加依赖于组件的生命周期。比如说PluginRegistry.Registrar.activity()
可能返回的是null,因为FlutterView可能并没有和任何Activity绑定。
换句话说,采用旧的API开发插件可能会产生不可预知的问题。目前,Flutter官方提供的插件均已完成迁移。
升级步骤
插件类实现FlutterPlugin接口,你也可以把FlutterPlugin和MethodCallHandler用两个类分别实现。插件需要保留registerWith方法来兼容使用老版本插件API的App。使用新插件的App将会调用FlutterPlugin的接口onAttachedToEngine来初始化插件,而未升级插件API的老版本的App调用registerWith方法实现注册。此外,对于所有公共非接口定义方法都应该补充文档。在混合开发的场景下,这些方法会对开发者完全开放。
(可选)如果插件需要获取Activity引用,需要实现ActivityAware。
(可选)如果需要持有后台服务实例,需要实现ServiceAware。
入口Flutter Activity需要继承v2 embedding FlutterActivity。具体可以看另一篇升级准1.12的Android项目,需要注意的是新版本的插件类最好提供一个公共构造方法。
1
2
3
4
5
6
7
8
9
10package 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,需要在AndroidManifest.xml中使用
io.flutter.embedding.android.FlutterActivity
。1
2
3
4
5
6
7
8
9
10
11
12
13
14<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: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 embedding API的EmbeddingV1Activity.java类来测试是否兼容v1 embedding的插件。需要注意的时候,必须要手动注册插件而不是使用
GeneratedPluginRegistrant。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package io.flutter.plugins.batteryexample;
import android.os.Bundle;
import dev.flutter.plugins.e2e.E2EPlugin;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.battery.BatteryPlugin;
public class EmbeddingV1Activity extends FlutterActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BatteryPlugin.registerWith(registrarFor("io.flutter.plugins.battery.BatteryPlugin"));
E2EPlugin.registerWith(registrarFor("dev.flutter.plugins.e2e.E2EPlugin"));
}
}
添加
<meta-data android:name="flutterEmbedding" android:value="2"/>
到AndroidManifest.xml中,这样项目就会使用v2 embedding。(可选) 如果创建了EmbeddingV1Activity类,需要吧它加到AndroidManifest.xml文件中。
1
2
3
4
5
6
7<activity
android:name=".EmbeddingV1Activity"
android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
</activity>
测试插件
我们鼓励对插件进行充分测试,虽然不是必要的,但是我们鼓励开发者这么做。
把build.gradle文件中的android.support.test替换成androidx.test。
1
2
3
4
5defaultConfig {
...
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
...
}1
2
3
4
5
6
7dependencies {
...
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
添加测试文件。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package 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;
public class MainActivityTest {
// Replace `MainActivity` with `io.flutter.embedding.android.FlutterActivity` if you removed `MainActivity`.
public ActivityTestRule<MainActivity> rule = new
ActivityTestRule<>(MainActivity.class);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14package 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;
public class EmbeddingV1ActivityTest {
public ActivityTestRule<EmbeddingV1Activity> rule =
new ActivityTestRule<>(EmbeddingV1Activity.class);
}添加
e2e
和flutter_driver
dev_dependencies到/pubspec.yaml
和/example/pubspec.yaml
。1
2
3e2e: ^0.2.1
flutter_driver:
sdk: flutter更新Flutter的最低支持版本,目前所有新v2 embedding插件最小支持版本均已升级至1.12.13+hotfix.6版本。
1
2
3
4environment:
sdk: ">=2.0.0-dev.28.0 <3.0.0"
flutter: ">=1.12.13+hotfix.6 <2.0.0"
在
<plugin_name>/test
目录下创建测试文件<plugin_name>_e2e.dart
。下面的测试针对的是插件是否成功注册到v2 embedder。1
2
3
4
5
6
7
8
9
10
11
12
13import '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);
});
}也可以在Native运行测试:
1
2
3
4
5cd <plugin_name>/example
flutter build apk
cd android
./gradlew app:connectedAndroidTest -Ptarget=`pwd`/../../test/<plugin_name>_e2e.dart
基础插件
只实现FlutterPlugin接口
1 | public class MyPlugin implements FlutterPlugin { |
在onAttachedToEngine方法内初始化插件,在onDetachedFromEngine方法内清理插件引用。
FlutterPluginBinding有两个很重要的方法:
- binding.getFlutterEngine(),返回的是绑定的FlutterEngine对象,可以通过它继而拿到DartExecutor,FlutterRenderer等。
- bingding.getApplicationContext(),返回的是Applicaton‘s Context。
UI/Activity 插件
如果需要与UI交互,比如申请权限或者改变Android UI,那么你需要实现ActivityAware接口。
1 | public class MyPlugin implements FlutterPlugin, ActivityAware { |
为了和Activity交互,你的ActivityAware插件需要经历4个阶段。
- 首先是onAttachedToActivity,此时可以通过ActivityPluginBinding获取Activity实例和一系列的回调。
- 当发生configuration change时,你必须在onDetachedFromActivityConfigChanges回调中做一些清理工作。
- 在onReattachedToActivityForConfigChanges回调中重新做一些初始化工作。
- 在onDetachedFromActivity中必须把所有Activity引用都清理掉,返回到无UI配置中。