Flutter 插件 从启动到插件开始执行

2020-05-14 0:00:26
Flutter 插件,FlutterPlugin,FlutterEngine

Android Plugin API 2.0

在 Flutter 1.12 ,插件 API 升 级到了 2.0 。
API 2.0 之前。
开发者通常要在 MainActivityonCreate() 方法中手动调用 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>

相关文章

参考资料

源代码

文章:

API文档:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342