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
API文档: