前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >flutter启动流程跟踪简析

flutter启动流程跟踪简析

原创
作者头像
brzhang
修改2019-12-03 15:06:44
1.7K0
修改2019-12-03 15:06:44
举报
文章被收录于专栏:玩转全栈玩转全栈玩转全栈

我们项目接入flutter由来已久,采用的是混栈开发的方式,没有办法,因为项目开发了比较久,全部替换为flutter实现想想也不太现实,混栈开发的过程中,我们遇到了一些问题,当时都是一脸懵逼,貌似最后都是很侥幸的通过万能的Google给汤坑汤沟去了,说来也是惭愧,本来很简单的事情,却需要花费一些不必要的时间,所以,当时就想如果有时间,一定要好好研究下flutter的原理,我们接入flutter,flutter到底是如何启动的,启动的过程中他做了一些什么,假如以后需要做性能优化,我们该从何处入手。

flutter启动流程简析

首先,这里有必要介绍一下我们接入flutter的方式,是的采用的是这种方式,相当于是直接字创建一个FlutterView,随后把这个FlutterView塞给一个Fragment即可。

我们这里采用Fragment的原因是,复用性,比如首页Tab页面需要替换为Flutter页面,我们可以直接上这个Fragment,然后,比较独立的业务需要使用activity的话,也是可以直接嵌入这个有FlutterView的Fragment的。暂且不说这种做法的缺点,我们先来了解一下FlutterView是怎么一步一步创建出来的。

当从native页打开一个flutter页,比如,你的首页的第三个tab是flutter,且启动App,进入首页,当你点击切换到第三个tab的时候,那么,此时就会触发这个创建FlutterView的流程。

FlutterMain.startInitialization

public static void startInitialization(@NonNull Context applicationContext, @NonNull FlutterMain.Settings settings) {
        if (!isRunningInRobolectricTest) {
            if (Looper.myLooper() != Looper.getMainLooper()) {
                throw new IllegalStateException("startInitialization must be called on the main thread");
            } else if (sSettings == null) {
                sSettings = settings;
                long initStartTimestampMillis = SystemClock.uptimeMillis();
                initConfig(applicationContext);
                initResources(applicationContext);
                System.loadLibrary("flutter");
                VsyncWaiter.getInstance((WindowManager)applicationContext.getSystemService("window")).init();
                long initTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis;
                FlutterJNI.nativeRecordStartTimestamp(initTimeMillis);
            }
        }
    }

这个方法的主要作用是加载了libflutter.so库,然后就是初始化了一些配置和资源,随后就是通过FLutterJNI通知native层,flutter已经准备启动。

FlutterMain.ensureInitializationComplete

public static void ensureInitializationComplete(@NonNull Context applicationContext, @Nullable String[] args) {
                try {
                    if (sResourceExtractor != null) {
                        sResourceExtractor.waitForCompletion();
                    }
                    List<String> shellArgs = new ArrayList();
                    shellArgs.add("--icu-symbol-prefix=_binary_icudtl_dat");
                    ApplicationInfo applicationInfo = getApplicationInfo(applicationContext);
                    shellArgs.add("--icu-native-lib-path=" + applicationInfo.nativeLibraryDir + File.separator + "libflutter.so");
                    if (args != null) {
                        Collections.addAll(shellArgs, args);
                    }
                    String kernelPath = null;
                    shellArgs.add("--aot-shared-library-name=" + sAotSharedLibraryName);
                    String engineCachesPath = PathUtils.getCacheDirectory(applicationContext);
                    FlutterJNI.nativeInit(applicationContext, (String[])shellArgs.toArray(new String[0]), (String)kernelPath, appStoragePath, engineCachesPath);
                    sInitialized = true;
                } catch (Exception var7) {
                    Log.e("FlutterMain", "Flutter initialization failed.", var7);
                    throw new RuntimeException(var7);
                }
            }
        }
    }

这个函数的作用是保证相关so、相关资源全部被加载完,因此,我们可以看到这样一条阻塞调用在此,sResourceExtractor.waitForCompletion,值得我们注意的是,此时的libflutter.so似乎又被加载了一次,不过,不是System.loadLibrary,而是调用FlutterJNI.nativeInit去加载的,而此时加载的还有libapp.so,这个libapp.so我们应该不会陌生,他就是我们用flutter写的业务逻辑所打包的产物,libflutter.so实际上可以理解为libapp.so执行所必备的环境之一。通常libflutter.so是有两个版本的,debug和release版本,因为flutter的debug和release执行的方式不一样,debug采用JIT,release采用AOT(性能更好),这就是release模式可以明显感觉比debug流程很多的原因。

创建FLutterNativeView

接下来的步骤是创建FlutterNativeView了。

public FlutterNativeView(@NonNull Context context, boolean isBackgroundView) {
        this.mContext = context;
        this.mPluginRegistry = new FlutterPluginRegistry(this, context);
        this.mFlutterJNI = new FlutterJNI();
        this.mFlutterJNI.setRenderSurface(new FlutterNativeView.RenderSurfaceImpl());
        this.dartExecutor = new DartExecutor(this.mFlutterJNI, context.getAssets());
        this.mFlutterJNI.addEngineLifecycleListener(new FlutterNativeView.EngineLifecycleListenerImpl());
        this.attach(this, isBackgroundView);
        this.assertAttached();
}

从FlutterNativeView的构造方法中,我们可以看到这里初始化了插件注册器FlutterPluginRegistry,然后创建了FlutterJNI,为FlutterJNI添加了RenderSurface,然后初始化了一个dart的执行环境DartExecutor。

private void attach(FlutterNativeView view, boolean isBackgroundView) {
        this.mFlutterJNI.attachToNative(isBackgroundView);
        this.dartExecutor.onAttachedToJNI();
}

最重要的是,将FlutterJNI和DartExecutor及FlutterNativeView进行了关联

  • 我们可以看到FlutterNativeView和FlutterJNI关联获得了一个nativePlatformViewId,通过这个nativePlatformViewId,flutter更新的UI我们得以在native层看到变化。
@UiThread
    public void attachToNative(boolean isBackgroundView) {
        this.ensureRunningOnMainThread();
        this.ensureNotAttachedToNative();
        this.nativePlatformViewId = this.nativeAttach(this, isBackgroundView);
 }
  • 我们还可以看到FlutterJNI和DartExecutor关联,拿到了一个消息通道,这为native和flutter沟通建立了一个桥梁。
public void onAttachedToJNI() {
        Log.v("DartExecutor", "Attached to JNI. Registering the platform message handler for this Dart execution context.");
        this.flutterJNI.setPlatformMessageHandler(this.messenger);
    }

所有,从整体上来说,把FLutterNativeView作为沟通FLutter和Native的桥梁也不为过。

随后一步,创建FlutterView

public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) {
        super(context, attrs);
        this.nextTextureId = new AtomicLong(0L);
        this.mIsSoftwareRenderingEnabled = false;
        this.didRenderFirstFrame = false;
        this.onAccessibilityChangeListener = new OnAccessibilityChangeListener() {
            public void onAccessibilityChanged(boolean isAccessibilityEnabled, boolean isTouchExplorationEnabled) {
                FlutterView.this.resetWillNotDraw(isAccessibilityEnabled, isTouchExplorationEnabled);
            }
        };
        Activity activity = getActivity(this.getContext());
        if (activity == null) {
            throw new IllegalArgumentException("Bad context");
        } else {
            if (nativeView == null) {
                this.mNativeView = new FlutterNativeView(activity.getApplicationContext());
            } else {
                this.mNativeView = nativeView;
            }

            this.dartExecutor = this.mNativeView.getDartExecutor();
            this.flutterRenderer = new FlutterRenderer(this.mNativeView.getFlutterJNI());
            this.mIsSoftwareRenderingEnabled = FlutterJNI.nativeGetIsSoftwareRenderingEnabled();
            this.mMetrics = new FlutterView.ViewportMetrics();
            this.mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density;
            this.setFocusable(true);
            this.setFocusableInTouchMode(true);
            this.mNativeView.attachViewAndActivity(this, activity);
            this.mSurfaceCallback = new Callback() {
                public void surfaceCreated(SurfaceHolder holder) {
                    FlutterView.this.assertAttached();
                    FlutterView.this.mNativeView.getFlutterJNI().onSurfaceCreated(holder.getSurface());
                }

                public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                    FlutterView.this.assertAttached();
                    FlutterView.this.mNativeView.getFlutterJNI().onSurfaceChanged(width, height);
                }

                public void surfaceDestroyed(SurfaceHolder holder) {
                    FlutterView.this.assertAttached();
                    FlutterView.this.mNativeView.getFlutterJNI().onSurfaceDestroyed();
                }
            };
            this.getHolder().addCallback(this.mSurfaceCallback);
            this.platformChannel = new PlatformChannel(this.dartExecutor);
            this.systemChannel = new SystemChannel(this.dartExecutor);
            this.settingsChannel = new SettingsChannel(this.dartExecutor);
            final PlatformPlugin platformPlugin = new PlatformPlugin(activity, this.platformChannel);
            this.addActivityLifecycleListener(new ActivityLifecycleListener() {
                public void onPostResume() {
                    platformPlugin.updateSystemUiOverlays();
                }
            });
            this.mImm = (InputMethodManager)this.getContext().getSystemService("input_method");
            PlatformViewsController platformViewsController = this.mNativeView.getPluginRegistry().getPlatformViewsController();
            this.mTextInputPlugin = new TextInputPlugin(this, this.dartExecutor, platformViewsController);
            this.androidKeyProcessor = new AndroidKeyProcessor(this.keyEventChannel, this.mTextInputPlugin);
            this.androidTouchProcessor = new AndroidTouchProcessor(this.flutterRenderer);
            this.sendUserPlatformSettingsToDart();
        }
    }

可以看到dartExecutor,flutterRenderer实际上都是前面FLutterNativeView的创建过程中的产物,此时都一一被FlutterView持有且运用,此外,很多Channel在此处被创建。比较重要的,flutter上面的触摸事件是如何处理的,在这里也有了呈现,那就是,触摸点击等事件被AndroidKeyProcessorAndroidTouchProcessor包装成了元数据发送给了flutter层去处理。

最后一步,添加回调,挂载第三方插件GeneratedPluginRegistrant.registerWith

lifecycle.addObserver(new LifecycleObserver() {
            @OnLifecycleEvent(Event.ON_CREATE)
            public void onCreate() {
                FlutterRunArguments arguments = new FlutterRunArguments();
                arguments.bundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext());
                arguments.entrypoint = "main";
                flutterView.runFromBundle(arguments);
                GeneratedPluginRegistrant.registerWith(flutterView.getPluginRegistry());
            }

            @OnLifecycleEvent(Event.ON_START)
            public void onStart() {
                flutterView.onStart();
            }

            @OnLifecycleEvent(Event.ON_RESUME)
            public void onResume() {
                flutterView.onPostResume();
            }

            @OnLifecycleEvent(Event.ON_PAUSE)
            public void onPause() {
                flutterView.onPause();
            }

            @OnLifecycleEvent(Event.ON_STOP)
            public void onStop() {
                flutterView.onStop();
            }

            @OnLifecycleEvent(Event.ON_DESTROY)
            public void onDestroy() {
                flutterView.destroy();
            }
        });

这里,flutter业务模块的创建的回调onCreate中,会注册第三方插件,仅仅被调用一次,这个回调也比较有用,当flutter页面切回到不可见是,比如从push一个原生页面覆盖了flutter页,会出发onPause--->onStop。

我们用一整张图来初略概括一下一个FlutterFragment的创建,大概申请了哪些资源。

因此,我们可以看到对于我们Native中,有几个这样的flutterfragment,就会有几份FlutterJNI,DartExecutor等等,这些和界面似乎无关的东西并没有被很好的重用。

所以推荐的flutter接入方式是什么?

在接入一段时间之后,我们发现,flutter接入层jar包中,有两个FlutterView,分别在不同的包目录下,我们上面的接入方式使用的是参考facade包下的FlutterFragment,内部的FlutterVIew直接继承自SurfaceView

public class FlutterView extends SurfaceView implements BinaryMessenger, TextureRegistry {
    private static final String TAG = "FlutterView";
    private final DartExecutor dartExecutor;
    private final FlutterRenderer flutterRenderer;

而,embadding包下面的FlutterVIew继承至FrameLayout

public class FlutterView extends FrameLayout {
    private static final String TAG = "FlutterView";
    @NonNull
    private FlutterView.RenderMode renderMode;
    @Nullable
    ......省略很多
    private void init() {
        Log.v("FlutterView", "Initializing FlutterView");
        switch(this.renderMode) {
        case surface:
            Log.v("FlutterView", "Internally using a FlutterSurfaceView.");
            FlutterSurfaceView flutterSurfaceView = new FlutterSurfaceView(this.getContext(), this.transparencyMode == FlutterView.TransparencyMode.transparent);
            this.renderSurface = flutterSurfaceView;
            this.addView(flutterSurfaceView);
            break;
        case texture:
            Log.v("FlutterView", "Internally using a FlutterTextureView.");
            FlutterTextureView flutterTextureView = new FlutterTextureView(this.getContext());
            this.renderSurface = flutterTextureView;
            this.addView(flutterTextureView);
        }

        this.setFocusable(true);
        this.setFocusableInTouchMode(true);
    }

但是最终还是通过addView的方式添加了surface或者是texture,可以看到,我们多了一种选择texture,至于surface或者texture的区别,可以参考这篇文章,通过Google这种布局方式来看,感觉用那种可以根据实际的应用场景来决定。

这里是Google给出的添加flutterfragment的方式,感觉已经是朝着引擎单例的模式在走,不过距离实现单例,任然需要我们自己动手做一些工作,具体效果如何还有待验证,这样下去,flutter页面多开内存会很恐怖的局面肯定是会解开的。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • flutter启动流程简析
  • FlutterMain.startInitialization
  • FlutterMain.ensureInitializationComplete
  • 创建FLutterNativeView
  • 随后一步,创建FlutterView
  • 最后一步,添加回调,挂载第三方插件GeneratedPluginRegistrant.registerWith
  • 所以推荐的flutter接入方式是什么?
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档