Flutter V0.154 Android 插件解析

1. Flutter Page和Android Activity/Fragment 生命周期同步

这个类的主要作用就是同步activity 和Flutter的生命周期

    @Override
    public void onCreate() {
        //....
        mState = STATE_CREATED;
        mContainer.getBoostFlutterView().onResume();
        mProxy.create();
    }

    @Override
    public void onAppear() {
         //....
        mState = STATE_APPEAR;
        mManager.pushRecord(this);
        mContainer.getBoostFlutterView().onAttach();
        mProxy.appear();
    }

    @Override
    public void onDisappear() {
         //....
        mState = STATE_DISAPPEAR;
        mProxy.disappear();
        if(getContainer().getContextActivity().isFinishing()) {
            mProxy.destroy();
        }
        mContainer.getBoostFlutterView().onDetach();
        mManager.popRecord(this);
    }

    @Override
    public void onDestroy() {
         //....
        mState = STATE_DESTROYED;
        mProxy.destroy();
        mContainer.getBoostFlutterView().onDestroy();
        mManager.removeRecord(this);
        mManager.setContainerResult(this,-1,-1,null);
        if (!mManager.hasContainerAppear()) {
            mContainer.getBoostFlutterView().onPause();
            mContainer.getBoostFlutterView().onStop();
        }
    }

    //....其他的生命周期

同步的生命周期主要有onAppear/onDisappear/onDestroy 等

还有一个就是methodProxy 通信代理类,告知flutter页面的生命周期的回调

private class MethodChannelProxy {
        private int mState = STATE_UNKNOW;
        private void create() {
            if (mState == STATE_UNKNOW) {
                invokeChannelUnsafe("didInitPageContainer",
                        mContainer.getContainerUrl(),
                        mContainer.getContainerUrlParams(),
                        mUniqueId
                );
                mState = STATE_CREATED;
            }
        }
         public void invokeChannelUnsafe(String method, String url, Map params, String uniqueId) {
            HashMap<String, Object> args = new HashMap<>();
            args.put("pageName", url);
            args.put("params", params);
            args.put("uniqueId", uniqueId);
            FlutterBoost.singleton().channel().invokeMethodUnsafe(method, args);
        }
    }
       //其他页面生命周期的回调......
    public static String genUniqueId(Object obj) {
        return System.currentTimeMillis() + "-" + obj.hashCode();
    }

之后总体会调用到BoostChannel类中的invokeMethodUnsafe/invokeMethod方法中

 public void invokeMethodUnsafe(final String name,Serializable args){
        invokeMethod(name, args, new MethodChannel.Result() {
            @Override
            public void success(@Nullable Object o) {
                //every thing ok...
            }
            @Override
            public void error(String s, @Nullable String s1, @Nullable Object o) {
                Debuger.log("invoke method "+name+" error:"+s+" | "+s1);
            }
            @Override
            public void notImplemented() {
                Debuger.log("invoke method "+name+" notImplemented");
            }
        });
    }

对应的APP整体的声明周期回调是通过Application.ActivityLifecycleCallbacks 这个回调函数来确认的

FlutterBoost构造函数注册了这个回调函数

具体代码如下

 class ActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
        @Override
        public void onActivityStarted(Activity activity) {
            if (mCurrentActiveActivity == null) {
                if (mEngineProvider.tryGetEngine() != null) {
                    HashMap<String, String> map = new HashMap<>();
                    map.put("type", "foreground");
                    channel().sendEvent("lifecycle",map);
                }
            }
            mCurrentActiveActivity = activity;
        }
        //....其他APP生命周期回调
    }

通过上面的invokeMethod会通过Channel调用到Flutter对应的boost_channel.dart对应的方法中

typedef Future<dynamic> EventListener(String name, Map arguments);
typedef Future<dynamic> MethodHandler(MethodCall call);
class BoostChannel {
  final MethodChannel _methodChannel = MethodChannel("flutter_boost");
  final Map<String, List<EventListener>> _eventListeners = Map();
  final Set<MethodHandler> _methodHandlers = Set();
  BoostChannel() {
    _methodChannel.setMethodCallHandler((MethodCall call){
      if (call.method == "__event__") {
        //取出对应的参数
        String name = call.arguments["name"];
        Map arg = call.arguments["arguments"];
        List<EventListener> list = _eventListeners[name];
        if (list != null) {
          for (EventListener l in list) {
            //App生命周期 循环调用给回调函数
            l(name, arg);
          }
        }
      }else{
        for(MethodHandler handler in _methodHandlers) {
        //页面Page生命周期 循环调用给回调函数
          handler(call);
        }
      }
      return Future.value();
    });
  }
 }

执行回调是FlutterBoost的构造函数调用ContainerCoordinator(_boostChannel)之后进入到了container_coordinator.dart中

 ContainerCoordinator(BoostChannel channel) {
    assert(_instance == null);
    _instance = this;
    channel.addEventListener("lifecycle",
        (String name, Map arguments) => _onChannelEvent(arguments));
    channel.addMethodHandler((MethodCall call) => _onMethodCall(call));
  }
  Future<dynamic> _onChannelEvent(dynamic event) {
    if (event is Map) {
      Map map = event;
      final String type = map['type'];
      switch (type) {
        case 'foreground':
          {
            FlutterBoost.containerManager?.setForeground();
          }
          break;
          //....其他App生命周期回调处理
      }
    }
  }
  
   Future<dynamic> _onMethodCall(MethodCall call) {
    switch (call.method) {
      case "didInitPageContainer":
        {
          String pageName = call.arguments["pageName"];
          Map params = call.arguments["params"];
          String uniqueId = call.arguments["uniqueId"];
          _nativeContainerDidInit(pageName, params, uniqueId);
        }
        break;
         //....其他页面生命周期回调处理
        }
  }
  

2.Flutter引擎复用逻辑

主要是体现在XFlutterView 的attachToFlutterEngine和detachFromFlutterEngine方法中

 public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) {
    Log.d(TAG, "attachToFlutterEngine()");
    if (isAttachedToFlutterEngine()) {
      if (flutterEngine == this.flutterEngine) {
        // 相同 复用之前的引擎
        return;
      }
      // 不同 移除之前的引擎
      detachFromFlutterEngine();
    }
    //重新赋值
    this.flutterEngine = flutterEngine;

    // 设置渲染层
    this.flutterEngine.getRenderer().attachToRenderSurface(renderSurface);

    // 重设输入输出
    textInputPlugin = new TextInputPlugin(
        this,
        this.flutterEngine.getDartExecutor()
    );
    //android 的键盘处理
    androidKeyProcessor = new AndroidKeyProcessor(
        this.flutterEngine.getKeyEventChannel(),
        textInputPlugin
    );
    //触摸处理
    androidTouchProcessor = new AndroidTouchProcessor(this.flutterEngine.getRenderer());
    //辅助连接桥
    accessibilityBridge = new AccessibilityBridge(
        this,
        flutterEngine.getAccessibilityChannel(),
        (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE),
        getContext().getContentResolver(),
        // TODO(mattcaroll): plumb the platform views controller to the accessibility bridge.
        // https://github.com/flutter/flutter/issues/29618
        null
    );
    
    accessibilityBridge.setOnAccessibilityChangeListener(onAccessibilityChangeListener);
    resetWillNotDraw(
        accessibilityBridge.isAccessibilityEnabled(),
        accessibilityBridge.isTouchExplorationEnabled()
    );

   //重启输入连接
    textInputPlugin.getInputMethodManager().restartInput(this);
    //一系列初始化
    // Push View and Context related information from Android to Flutter.
    sendUserSettingsToFlutter();
    sendLocalesToFlutter(getResources().getConfiguration());
    sendViewportMetricsToFlutter();
  }
  
  
  public void detachFromFlutterEngine() {
    if (!isAttachedToFlutterEngine()) {
      return;
    }

    //重启输入连接
    textInputPlugin.getInputMethodManager().restartInput(this);

    // 移除渲染层
    flutterEngine.getRenderer().detachFromRenderSurface();
    flutterEngine = null;
  }

引擎相同则复用,不同detach 之前,重新赋值以及初始化相关的配置

3.打开Flutter页面的主要逻辑

调用链 PageRouter.openPageByUrl -->BoostFlutterEngine.startRun -->BoostFlutterActivity.onCreate -->createFlutterView(mFlutterEngine)

BoostFlutterEngine.startRun

BoostFlutterEngine.startRun这个方法的启动主要是在Application 配置的启动的方式来决定的

    int IMMEDIATELY = 0;          //立即启动引擎
    int ANY_ACTIVITY_CREATED = 1; //当有任何Activity创建时,启动引擎

    @Override
    public int whenEngineStart() {
        return ANY_ACTIVITY_CREATED;
    }

默认使用这个ANY_ACTIVITY_CREATED 的时候,会在ActivityLifecycleCallbacks回调函数中做处理

  class ActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            if (platform().whenEngineStart() == IPlatform.ANY_ACTIVITY_CREATED) {
                sInstance.mEngineProvider
                        .provideEngine(activity)
                        .startRun(activity);
            }
        }
    }

BoostFlutterActivity.onCreate

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        configureWindowForTransparency();
        //创建同步器
        mSyncer = FlutterBoost.singleton().containerManager().generateSyncer(this);
        //创建引擎
        mFlutterEngine = createFlutterEngine();
        //创建FlutterView
        mFlutterView = createFlutterView(mFlutterEngine);
        setContentView(mFlutterView);
        //同步器初始化
        mSyncer.onCreate();
        //配置状态栏
        configureStatusBarForFullscreenFlutterExperience();
    }

同步器就是前面讲的ContainerRecord,主要是同步Flutter和Android的页面和APP状态

FlutterEngine,这个是Flutter的核心,flutter_boost对其做了进一步的封装

FlutterView 主要是展示Flutter中的Widget页面

4.FlutterEngine 封装

主要是修改DartExecutor.DartEntrypoint和 InitRoute 以及对应的插件注册

 public BoostFlutterEngine(@NonNull Context context, DartExecutor.DartEntrypoint entrypoint, String initRoute) {
        super(context);
        mContext = context.getApplicationContext();
        mBoostPluginRegistry = new BoostPluginRegistry(this, context);
        //支持用户的修改的进入点 一般对应的main
        if (entrypoint != null) {
            mEntrypoint = entrypoint;
        } else {
            mEntrypoint = defaultDartEntrypoint(context);
        }
        //初始化路由
        if (initRoute != null) {
            mInitRoute = initRoute;
        } else {
            mInitRoute = defaultInitialRoute(context);
        }
        //获取FlutterJNI用于引擎通信相关的
        FlutterJNI flutterJNI = null;
        try {
            Field field = FlutterEngine.class.getDeclaredField("flutterJNI");
            field.setAccessible(true);

            flutterJNI = (FlutterJNI) field.get(this);
        } catch (Throwable t) {
            try {
                for(Field field:FlutterEngine.class.getDeclaredFields()) {
                    field.setAccessible(true);
                    Object o = field.get(this);

                    if(o instanceof FlutterJNI) {
                        flutterJNI = (FlutterJNI)o;
                    }
                }

                if(flutterJNI == null) {
                    throw new RuntimeException("FlutterJNI not found");
                }
            }catch (Throwable it){
                Debuger.exception(it);
            }
        }
        mFakeRender = new FakeRender(flutterJNI);
    }

BoostRegistrar 插件注册主要是获取Activity,让Container 和Activity同步

        @Override
        public Activity activity() {
            Activity activity;
            IContainerRecord record;
            //获取当前的Container
            record = FlutterBoost.singleton().containerManager().getCurrentTopRecord();
            if (record == null) {
                //获取最后生成Container
                record = FlutterBoost.singleton().containerManager().getLastGenerateRecord();
            }
            if (record == null) {
                activity = FlutterBoost.singleton().currentActivity();
            } else {
                activity = record.getContainer().getContextActivity();
            }
            if (activity == null && mCurrentActivityRef != null) {
                activity = mCurrentActivityRef.get();
            }
            if (activity == null) {
                throw new RuntimeException("current has no valid Activity yet");
            }
            return activity;

5.BoostFlutterView封装View

BoostFlutterView主要是封装各种Flutter需要加载的东西

  private void init() {
        //创建引擎
        if (mFlutterEngine == null) {
            mFlutterEngine = createFlutterEngine(getContext());
        }
        //activity传递的参数
        if (mArguments == null) {
            mArguments = new Bundle();
        }
        //FlutterBoost平台自身需要的参数
        mPlatformPlugin = new PlatformPlugin((Activity) getContext(), mFlutterEngine.getPlatformChannel());
        //封装的XFlutterView
        mFlutterView = new XFlutterView(getContext(), getRenderMode(), getTransparencyMode());
        //添加控件
        addView(mFlutterView, new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        //截屏View
        mSnapshot = new SnapshotView(getContext());
        //Flutter引擎初始化loading
        if(mRenderingProgressCoverCreator != null) {
            mRenderingProgressCover = mRenderingProgressCoverCreator
                    .createRenderingProgressCover(getContext());
        }else{
            mRenderingProgressCover = createRenderingProgressCorver();
        }
        //添加loading
        if(mRenderingProgressCover != null) {
            addView(mRenderingProgressCover, new FrameLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        }
        //监听第一帧绘制回调函数
        mFlutterView.addOnFirstFrameRenderedListener(mOnFirstFrameRenderedListener);
        //启动封装引擎
        mFlutterEngine.startRun((Activity)getContext());

        final IStateListener stateListener = FlutterBoost.sInstance.mStateListener;
        if(stateListener != null) {
            stateListener.onFlutterViewInited(mFlutterEngine,this);
        }
        checkAssert();
    }

默认的createRenderingProgressCorver返回的是一个ProgressBar 用户可以根据重写这个方法自己修改loading界面

结语

还有一些相关的类 XAndroidKeyProcessor(按键处理)/XInputConnectionAdaptor(输入输出适配)/XTextInputPlugin(输入插件)。总体来讲,flutter_boost的Android版本插件难度不是很大,Flutter Page/App生命周期同步以及引擎封装思路还是不错的,可以像WebView一样的使用是该插件的初衷。以后有时间还有进一步的解读源码。

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

推荐阅读更多精彩内容

  • 下午,儿子看《乐高忍者:幻影旋转术大师》,正在看书的我瞟了几眼后竟然不可救药地喜欢上了,于是,陪儿子一起看了起来。...
    陌上花开5969阅读 1,783评论 0 1
  • 我其实并不喜欢酒 我只是喜欢微醉的感觉 把酒当作感情的一种宣泄 同样我写东西也是 有时它的直接并不亚于文字 偶尔带...
    牛大馕阅读 508评论 3 5
  • 昨日被伙伴圈发来的陈意涵35岁生日震惊到,35岁仍活得像个少女,这是多少人梦寐以求的事情? 让我每次能震惊的不只是...
    马田心Martinc手作阅读 257评论 3 0
  • 民谣用最朴素的词 内心深处的哼唱 很容易让人产生共勉 民谣编曲伴奏很简单 配器主要吉他为主 不停按规律弹 在恰当的...
    爱_22bb阅读 4,459评论 1 3