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一樣的使用是該插件的初衷。以後有時間還有進一步的解讀源碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章