聊聊Flutter Platform SDK

[TOC]

前言

​ 从Flutter的架构图中可以看出,Flutter Platform SDK处于整个Flutter框架的上层,连接了Java与Dart代码。那么作为“最上层”的它,到底扮演了哪些角色,以及是如何扮演好这些角色的呢?Google工程师用一个封装好的flutter.jar包"show me the answer"。

Platform SDK 的角色扮演

​ 通过对Platform SDK(以下简称 Platform)源码的阅读,可以大致将它分成三个角色:Creator(创建者),Transmitter(传递者),Registrant(注册人)。

Creator

创建者

FlutterMain
时序图
flutter_seq.png

FlutterMain担任着flutter的初始化工作,在被Application的onCreate调起后

  • initConfig

    必须的配置文件名称和路径

    • 通过命令行 flutter build aot生成的文件

      名称 内容
      isolate_snapshot_instr 应用程序指令段
      isolate_snapshot_data 应用程序数据段
      vm_snapshot_instr VM 虚拟机指令段
      vm_snapshot_data VM 虚拟机数据段

    ​ 注:详细含义参见官方说明

    • 路径信息

      flutter_assets等文件的路径信息

  • initAot

    1. 初始化标记位:snapshot文件集成的方式。
    2. sIsPrecompiledAsSharedLibrary 代表的是把所有的snapshot文件打包成一个动态库(一种类似ios的集成方式)。
    3. 禁止同时采用两种集成方式。
  • initResource

    1. 创建了一个ResourceExtractor 对象,他是Resource文件的搬运工
    2. 通过ResourceExtractor 对象的addResource方法初始化需要搬运的文件
    3. ResourceExtractor启动异步任务把asset下面的文件搬运到DataDir的flutter目录下面

FlutterActivityDelegate

WhatDelegate
//FlutterActivity.java
......
private final FlutterActivityEvents eventDelegate;
private final Provider viewProvider;
private final PluginRegistry pluginRegistry;   
private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this);
......
public FlutterActivity() {
        this.eventDelegate = this.delegate;
        this.viewProvider = this.delegate;
        this.pluginRegistry = this.delegate;
    }
......

我们可以从上面FlutterActivity的构造函数中看出Delegate到底代理了哪些职责

  • FlutterActivityEvents:将Activity生命周期的具体处理逻辑传递给FlutterView

  • Provider:在onCreate中创建的FlutterView,通过实现Provider的接口暴露出来

  • PluginRegistry:插件信息的注册与查询传递给FlutterView

    从上面看来,其实FlutterActivityDelegate直接交互的对象就是FlutterView,通过这样的设计,提高了每一个类的内聚性

flutter内聚.png

FlutterView

可以先简单的把FlutterView看成是负责显示的,而把后面的FlutterNativeView看成是负责通信的。

  • 解偶系统控件

    首先我们可以看到FlutterView是继承自SurfaceView的,提到SurfaceView我们肯定会想到“挖洞”,“双缓冲”这些词,也正是因为这些特性FlutterView可以很好的把UI渲染工作交给Flutter( 后者通过dart->组装LayerTree->Skia完成绘制 )。

1655b109f83bea9b.png
  • 传递状态消息给Dart UI

    这些消息大致可以将它们分成七个模块,两大类

    1. 通过反射原生系统的api进行数据通讯

      多语言模块、系统信息和用户设置

    2. 将数据打包成特殊格式以消息的形式和使用Dart编写的flutter控件交互

      生命周期、按键消息、路由导航和平台插件

FlutterNativeView

可以先简单的把FlutterNativeView看成是负责通信的。它在系统层面上实现了BinaryMessenger接口 [这在下文Transmitter中详细介绍]。

Transmitter

传递者

消息渠道

  • Flutter针对不同的应用场景封装了3类Channel

    1. MethodChannel:调用方法
    2. BasicMessageChannel:自定义结构信息
    3. EventChannel:事件的通知
  • 3类Channel拥有相似的结构类型

    1. 信使BinaryMessenger:我们自己创建的channel一般就是FlutterNativeView
    2. channel 名:channel的key值,不可重复
    3. MethodCodec /MessageCodec 解码器: 针对不同的channle解码二进制应答数据
channel.png

消息编解码

​ 标准平台通道使用一个标准的消息编解码器。简单的JSON类值(如布尔值)的高效二进制序列化,数字、字符串、字节缓冲区以及这些列表和映射。(查看细节 StandardMessageCodec)。这些值的序列化和反序列化在消息发送和接收值时自动发生。(对应的数据转化关系如下)

解码.jpg

传递流程

​ 通过Flutter Engine的数据转换,使得Dart和Android之间可以进行通信

channel流程图.png

Registrant

注册人

情境转换

​ 首先这里有三个形似得英文单词registry, registrar and registrant分别对应注册局,注册商和注册人。把它们翻译到现实的生活场景中的角色其实是一个“注册人通过注册商,更新注册信息后,注册商把信息传递给注册局进行保存”的过程。下面我们把这个过程再翻译回代码:

首先我们新建一个plugin插件,作为说明的对象:

// 实现 PluginRegistry.ActivityResultListener
public class FlutterMusicPlugin implements MethodCallHandler, PluginRegistry.ActivityResultListener {
    ...
    public static void registerWith(Registrar registrar) {
        //传入Activity
        final FlutterMusicPlugin plugin = new FlutterMusicPlugin(registrar.activity());
        ...
        // 注册ActivityResult回调
        registrar.addActivityResultListener(plugin);
    }
    
    @Override
    public void onMethodCall(MethodCall call, Result result) {
           ...
    }
    
    @Override
    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
           ...
        return false;
    }
}

同时在GeneratedPluginRegistrant类中会自动生成

public final class GeneratedPluginRegistrant {
  public static void registerWith(PluginRegistry registry) {
    if (alreadyRegisteredWith(registry)) {
      return;
    }
 FlutterMusicPlugin.registerWith(registry.registrarFor("com.plugin.FlutterMusicPlugin"));
  }

  private static boolean alreadyRegisteredWith(PluginRegistry registry) {
    final String key = GeneratedPluginRegistrant.class.getCanonicalName();
    if (registry.hasPlugin(key)) {
      return true;
    }
    registry.registrarFor(key);
    return false;
  }
}

注册人

​ 它对应的是代码中的GeneratedPluginRegistrant类

  • 发起注册

    GeneratedPluginRegistrant类中的

     FlutterMusicPlugin.registerWith(registry.registrarFor("com.plugin.FlutterMusicPlugin"));//即为发起注册点
    
  • 通过注册商更新注册

    FlutterMusicPlugin类中的

    registrar.addActivityResultListener(plugin);//即为通过注册商更新了需要Activity回调的信息
    
  • 同步给注册局

    sdk中的FlutterRegistrar类

    FlutterPluginRegistry.this.mActivityResultListeners.add(listener);//即为把信息同步给了注册局
    

注册商

​ 它对应的是代码中的FlutterPluginRegistry内部类FlutterRegistrar。

​ 不看不知道,"注册商"其实给我们提供了很多功能,比如获取activity,viewDestory的生命周期的回调,获取surfaceTexture等等,真是一个能力强大的"注册商"。

      
        Activity activity();//返回 Host app的Activity
        Context context();//返回 Application Context.
        Context activeContext();//返回 活动Context
        //返回 BinaryMessenger 主要用来注册Platform channels
        BinaryMessenger messenger();
        //返回 TextureRegistry,从里面可以拿到SurfaceTexture 
        TextureRegistry textures();
        //返回 当前Host app创建的FlutterView
        FlutterView view();
        //返回Asset对应的文件路径
        String lookupKeyForAsset(String var1);
        //返回Asset对应的文件路径
        String lookupKeyForAsset(String var1, String var2);
        //插件对外发布的一个"值"
        PluginRegistry.Registrar publish(Object var1);
        //注册权限相关的回调
        PluginRegistry.Registrar addRequestPermissionsResultListener(PluginRegistry.RequestPermissionsResultListener var1);
        //注册ActivityResult回调
        PluginRegistry.Registrar addActivityResultListener(PluginRegistry.ActivityResultListener var1);
        //注册NewIntent回调
        PluginRegistry.Registrar addNewIntentListener(PluginRegistry.NewIntentListener var1);
        //注册UserLeaveHint回调
        PluginRegistry.Registrar addUserLeaveHintListener(PluginRegistry.UserLeaveHintListener var1);
        //注册View销毁回调
        PluginRegistry.Registrar addViewDestroyListener(PluginRegistry.ViewDestroyListener var1);

注册局

​ 它对应的是代码中的FlutterPluginRegistry

pluginregister.png

从成员变量可以看出FlutterPluginRegistry主要维护两类信息:

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

推荐阅读更多精彩内容