2020-05-14 0:00:26
Flutter 插件,FlutterPlugin,FlutterEngine
Android Plugin API 2.0
在 Flutter 1.12 ,插件 API 升 级到了 2.0 。
API 2.0 之前。
开发者通常要在 MainActivity
的 onCreate()
方法中手动调用 GeneratedPluginRegistrant.registerWith()
来执行插件类中的一个静态方法初始化插件。
API 2.0 之后,出现了 FlutterPlugin 接口。
开发者只要让创建的插件类实现它,并把初始化代码放到一个重写的方法中就好了。
甚至都不需要创建 MainActivity
。
从 Flutter 启动到开始执行插件
随 FlutterPlugin 一同出现的还有新的 FlutterActivity 和 FlutterEngine 。
FlutterActivity 是什么
启动一个 Android 应用,启动的通常是一个 Activity 。
如果这个应用是 Flutter 应用,那么这个 Activity 是 FlutterActivity 。
Activity 创建后会先执行 onCreate()
方法。
在 FlutterActivity 的 onCreate()
方法中,创建了一个 FlutterEngine 。
FlutterEngine 是什么
官方文档对 FlutterEngine 的解释:
A single Flutter execution environment.
The FlutterEngine is the container through which Dart code can be run in an Android application.
是一个单独的 Flutter 执行环境,通过它,可以让一个 Android 应用运行 Dart 代码。
FlutterEngine 提供了
FlutterRenderer 负责渲染视图。
DartExecutor 作为 BinaryMessenger 用来和 Flutter 端通信。我们创建 MethodChannel 就要用到这个。
FlutterEnginePluginRegistry 作为 PluginRegistry 来登记开发者创建的 FlutterPlugin 。当我们在项目的
pubspec.yaml
文件中添加了插件,运行flutter pub get
后,会在安卓平台生成一个 GeneratedPluginRegistrant 类,该类中就用到了 PluginRegistry 来注册 FlutterPlugin 。PlatformViewsController 用来管理提供给 Flutter 端的原生平台视图。当我们想让 Flutter 应用中显示安卓原生视图的时候,例如百度地图,就会用到。
创建完 FlutterEngine ,FlutterActivity 的 onCreate()
方法还没有结束。
登记插件
接下来会执行 FlutterActivity
中的 configureFlutterEngine()
方法。
并把 FlutterEngine 作为参数传递给它。
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
registerPlugins(flutterEngine);
}
private static void registerPlugins(@NonNull FlutterEngine flutterEngine) {
try {
Class<?> generatedPluginRegistrant =
Class.forName("io.flutter.plugins.GeneratedPluginRegistrant");
Method registrationMethod =
generatedPluginRegistrant.getDeclaredMethod("registerWith", FlutterEngine.class);
registrationMethod.invoke(null, flutterEngine);
} catch (Exception e) {
Log.w(
TAG,
"Tried to automatically register plugins with FlutterEngine ("
+ flutterEngine
+ ") but could not find and invoke the GeneratedPluginRegistrant.");
}
}
这里调用了 GeneratedPluginRegistrant 中的 registerWith()
来登记插件。
并且给了 FlutterEngine 作为参数。
下面的内容就和我们写的插件相关了。
GeneratedPluginRegistrant
这个类是根据 pubspec.yaml
中的依赖生成的。
新建一个插件后,示例中的 pubspec.yaml
:
# <plugin_name>/example/pubspec.yaml
dependencies:
flutter:
sdk: flutter
flutter_plugin_demo: # 示例依赖了新建的插件
path: ../
插件的 pubspec.yaml
:
# <plugin_name>/pubspec.yaml
name: flutter_plugin_demo
flutter:
plugin:
platforms:
android:
package: xyz.waixingjiandie.flutter_plugin_demo
pluginClass: FlutterPluginDemoPlugin # 指定了 Android 插件的类名字
ios:
pluginClass: FlutterPluginDemoPlugin
自动生成的 GeneratedPluginRegistrant :
package io.flutter.plugins;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import io.flutter.embedding.engine.FlutterEngine;
/**
* Generated file. Do not edit.
* This file is generated by the Flutter tool based on the
* plugins that support the Android platform.
*/
@Keep
public final class GeneratedPluginRegistrant {
public static void registerWith(@NonNull FlutterEngine flutterEngine) {
flutterEngine.getPlugins().add(new xyz.waixingjiandie.flutter_plugin_demo.FlutterPluginDemoPlugin());
}
}
通过 FlutterEngine 的 getPlugins()
方法返回一个 PluginRegistry 。
这个 PluginRegistry 是新版,没有 hasPlugin()
方法。
通过它的 add()
方法登记插件,会自动检查是否已经登记过。
插件的登记,其实就是把插件添加到一个 Map 里。
这样就算把 FlutterPlugin 附加到了 FlutterEngine 。
插件登记后,会执行插件的 onAttachedToEngine()
方法。
并传递一个 FlutterPluginBinding 作为参数。
然后程序的运行就进入了 FlutterPlugin 。
FlutterPlugin
插件的代码位于 <plugin_name>/android/
目录下。
插件有2个方法:
onAttachedToEngine()
onDetachedFromEngine()
根据名字就能看出来,
一个是插件被关联到 FlutterEngine 时调用,可以做一些初始化工作。
另一个是插件从 FlutterEngine 移除时调用,可以在这里做一些清理工作。
他们都接收一个 FlutterPluginBinding 作为参数。
这个 FlutterPluginBinding 是 FlutterEngine 创建 PluginRegistry 时,
由 PluginRegistry 的实现 FlutterEnginePluginRegistry 创建的。
通过 FlutterPluginBinding 可以得到
- Application context
- BinaryMessenger
- PlatformViewRegistry
等常用对象。
自带的示例在这里创建了一个 MethodChannel 用来和 Flutter 通信。
创建 MethodChannel 就要用到 BinaryMessenger 。
可以通过 FlutterPluginBinding 的 getBinaryMessenger()
方法得到。
如果插件需要用到 Activity ,可以让插件再实现一个 ActivityAware 接口。
如果涉及后台服务,还有个 ServiceAware 接口。
用法可以参考官方插件 。
目前我只写了一个百度地图插件,参考了谷歌地图,需要用到 ActivityAware 。
以上就是从 app 启动到插件开始执行的过程了。
新的 API 还提出了 Federated plugins 的概念。
Federated plugins
将一个插件包,分离成了3类。
- 插件原生平台端提供的功能声明成接口放在一个包中
- 特定平台实现接口的包,可以有多个
- 插件为 app 提供各种功能的包,插件使用者调用这个包中的方法。
接口为这个包提供一个平台的实现,这个包调用实现中的方法。
见另一篇 Federated plugins
可以简化的东西
这是新建项目后示例的清单文件(有省略):
<!-- <plugin_name>/example/android/app/src/main/AndroidManifest.xml -->
<application
android:name="io.flutter.app.FlutterApplication"
<activity
android:name=".MainActivity"
</activity>
</application>
MainActivity.java
:
public class MainActivity extends FlutterActivity {
}
MainActivity 我并没有省略,就是空的。
参考官方的升级指南
我们可以在清单文件中把 .MainActivity
改成 io.flutter.embedding.android.FlutterActivity
。
然后删掉 MainActivity.java
文件。
清单文件中的 FlutterApplication 也可以去掉。于是变成了这样子:
<application
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
</activity>
</application>
相关文章
参考资料
源代码
文章:
- Modern Flutter Plugin Development
- Supporting the new Android plugins APIs
- Upgrading pre 1.12 Android projects