Flutter V0.154 Android 插件解析

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

這個(gè)類的主要作用就是同步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 等

還有一個(gè)就是methodProxy 通信代理類帚稠,告知flutter頁(yè)面的生命周期的回調(diào)

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);
        }
    }
       //其他頁(yè)面生命周期的回調(diào)......
    public static String genUniqueId(Object obj) {
        return System.currentTimeMillis() + "-" + obj.hashCode();
    }

之后總體會(huì)調(diào)用到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");
            }
        });
    }

對(duì)應(yīng)的APP整體的聲明周期回調(diào)是通過Application.ActivityLifecycleCallbacks 這個(gè)回調(diào)函數(shù)來確認(rèn)的

FlutterBoost構(gòu)造函數(shù)注冊(cè)了這個(gè)回調(diào)函數(shù)

具體代碼如下

 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生命周期回調(diào)
    }

通過上面的invokeMethod會(huì)通過Channel調(diào)用到Flutter對(duì)應(yīng)的boost_channel.dart對(duì)應(yīng)的方法中

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__") {
        //取出對(duì)應(yīng)的參數(shù)
        String name = call.arguments["name"];
        Map arg = call.arguments["arguments"];
        List<EventListener> list = _eventListeners[name];
        if (list != null) {
          for (EventListener l in list) {
            //App生命周期 循環(huán)調(diào)用給回調(diào)函數(shù)
            l(name, arg);
          }
        }
      }else{
        for(MethodHandler handler in _methodHandlers) {
        //頁(yè)面Page生命周期 循環(huán)調(diào)用給回調(diào)函數(shù)
          handler(call);
        }
      }
      return Future.value();
    });
  }
 }

執(zhí)行回調(diào)是FlutterBoost的構(gòu)造函數(shù)調(diào)用ContainerCoordinator(_boostChannel)之后進(jìn)入到了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生命周期回調(diào)處理
      }
    }
  }
  
   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;
         //....其他頁(yè)面生命周期回調(diào)處理
        }
  }
  

2.Flutter引擎復(fù)用邏輯

主要是體現(xiàn)在XFlutterView 的attachToFlutterEngine和detachFromFlutterEngine方法中

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

    // 設(shè)置渲染層
    this.flutterEngine.getRenderer().attachToRenderSurface(renderSurface);

    // 重設(shè)輸入輸出
    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;
  }

引擎相同則復(fù)用康二,不同detach 之前,重新賦值以及初始化相關(guān)的配置

3.打開Flutter頁(yè)面的主要邏輯

調(diào)用鏈 PageRouter.openPageByUrl -->BoostFlutterEngine.startRun -->BoostFlutterActivity.onCreate -->createFlutterView(mFlutterEngine)

BoostFlutterEngine.startRun

BoostFlutterEngine.startRun這個(gè)方法的啟動(dòng)主要是在Application 配置的啟動(dòng)的方式來決定的

    int IMMEDIATELY = 0;          //立即啟動(dòng)引擎
    int ANY_ACTIVITY_CREATED = 1; //當(dāng)有任何Activity創(chuàng)建時(shí),啟動(dòng)引擎

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

默認(rèn)使用這個(gè)ANY_ACTIVITY_CREATED 的時(shí)候,會(huì)在ActivityLifecycleCallbacks回調(diào)函數(shù)中做處理

  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();
        //創(chuàng)建同步器
        mSyncer = FlutterBoost.singleton().containerManager().generateSyncer(this);
        //創(chuàng)建引擎
        mFlutterEngine = createFlutterEngine();
        //創(chuàng)建FlutterView
        mFlutterView = createFlutterView(mFlutterEngine);
        setContentView(mFlutterView);
        //同步器初始化
        mSyncer.onCreate();
        //配置狀態(tài)欄
        configureStatusBarForFullscreenFlutterExperience();
    }

同步器就是前面講的ContainerRecord,主要是同步Flutter和Android的頁(yè)面和APP狀態(tài)

FlutterEngine,這個(gè)是Flutter的核心,flutter_boost對(duì)其做了進(jìn)一步的封裝

FlutterView 主要是展示Flutter中的Widget頁(yè)面

4.FlutterEngine 封裝

主要是修改DartExecutor.DartEntrypoint和 InitRoute 以及對(duì)應(yīng)的插件注冊(cè)

 public BoostFlutterEngine(@NonNull Context context, DartExecutor.DartEntrypoint entrypoint, String initRoute) {
        super(context);
        mContext = context.getApplicationContext();
        mBoostPluginRegistry = new BoostPluginRegistry(this, context);
        //支持用戶的修改的進(jìn)入點(diǎn) 一般對(duì)應(yīng)的main
        if (entrypoint != null) {
            mEntrypoint = entrypoint;
        } else {
            mEntrypoint = defaultDartEntrypoint(context);
        }
        //初始化路由
        if (initRoute != null) {
            mInitRoute = initRoute;
        } else {
            mInitRoute = defaultInitialRoute(context);
        }
        //獲取FlutterJNI用于引擎通信相關(guān)的
        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 插件注冊(cè)主要是獲取Activity,讓Container 和Activity同步

        @Override
        public Activity activity() {
            Activity activity;
            IContainerRecord record;
            //獲取當(dāng)前的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() {
        //創(chuàng)建引擎
        if (mFlutterEngine == null) {
            mFlutterEngine = createFlutterEngine(getContext());
        }
        //activity傳遞的參數(shù)
        if (mArguments == null) {
            mArguments = new Bundle();
        }
        //FlutterBoost平臺(tái)自身需要的參數(shù)
        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));
        }
        //監(jiān)聽第一幀繪制回調(diào)函數(shù)
        mFlutterView.addOnFirstFrameRenderedListener(mOnFirstFrameRenderedListener);
        //啟動(dòng)封裝引擎
        mFlutterEngine.startRun((Activity)getContext());

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

默認(rèn)的createRenderingProgressCorver返回的是一個(gè)ProgressBar 用戶可以根據(jù)重寫這個(gè)方法自己修改loading界面

結(jié)語(yǔ)

還有一些相關(guān)的類 XAndroidKeyProcessor(按鍵處理)/XInputConnectionAdaptor(輸入輸出適配)/XTextInputPlugin(輸入插件)莱革【撸總體來講,flutter_boost的Android版本插件難度不是很大盅视,F(xiàn)lutter Page/App生命周期同步以及引擎封裝思路還是不錯(cuò)的捐名,可以像WebView一樣的使用是該插件的初衷。以后有時(shí)間還有進(jìn)一步的解讀源碼闹击。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末镶蹋,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子赏半,更是在濱河造成了極大的恐慌贺归,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件断箫,死亡現(xiàn)場(chǎng)離奇詭異拂酣,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)仲义,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門婶熬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人光坝,你說我怎么就攤上這事∩模” “怎么了盯另?”我有些...
    開封第一講書人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)洲赵。 經(jīng)常有香客問我鸳惯,道長(zhǎng),這世上最難降的妖魔是什么叠萍? 我笑而不...
    開封第一講書人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任芝发,我火速辦了婚禮,結(jié)果婚禮上苛谷,老公的妹妹穿的比我還像新娘辅鲸。我一直安慰自己,他們只是感情好腹殿,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開白布独悴。 她就那樣靜靜地躺著,像睡著了一般锣尉。 火紅的嫁衣襯著肌膚如雪刻炒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,696評(píng)論 1 312
  • 那天自沧,我揣著相機(jī)與錄音坟奥,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛爱谁,可吹牛的內(nèi)容都是我干的晒喷。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼管行,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼厨埋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起捐顷,我...
    開封第一講書人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤荡陷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后迅涮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體废赞,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年叮姑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了唉地。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡传透,死狀恐怖耘沼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情朱盐,我是刑警寧澤群嗤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站兵琳,受9級(jí)特大地震影響狂秘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜躯肌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一者春、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧清女,春花似錦钱烟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至迄沫,卻和暖如春稻扬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背羊瘩。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工泰佳, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盼砍,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓逝她,卻偏偏與公主長(zhǎng)得像浇坐,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子黔宛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 下午近刘,兒子看《樂高忍者:幻影旋轉(zhuǎn)術(shù)大師》,正在看書的我瞟了幾眼后竟然不可救藥地喜歡上了臀晃,于是觉渴,陪兒子一起看了起來。...
    陌上花開5969閱讀 1,852評(píng)論 0 1
  • 我其實(shí)并不喜歡酒 我只是喜歡微醉的感覺 把酒當(dāng)作感情的一種宣泄 同樣我寫東西也是 有時(shí)它的直接并不亞于文字 偶爾帶...
    牛大馕閱讀 525評(píng)論 3 5
  • 昨日被伙伴圈發(fā)來的陳意涵35歲生日震驚到徽惋,35歲仍活得像個(gè)少女案淋,這是多少人夢(mèng)寐以求的事情? 讓我每次能震驚的不只是...
    馬田心Martinc手作閱讀 276評(píng)論 3 0
  • 民謠用最樸素的詞 內(nèi)心深處的哼唱 很容易讓人產(chǎn)生共勉 民謠編曲伴奏很簡(jiǎn)單 配器主要吉他為主 不停按規(guī)律彈 在恰當(dāng)?shù)?..
    愛_22bb閱讀 4,581評(píng)論 1 3