Flutter V0.154 Android 插件解析

Posted by lizubing1992 on 11-12,2019

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一样的使用是该插件的初衷。以后有时间还有进一步的解读源码。


0评论