我们项目接入flutter由来已久,采用的是混栈开发
的方式,没有办法,因为项目开发了比较久,全部替换为flutter实现想想也不太现实,混栈开发的过程中,我们遇到了一些问题,当时都是一脸懵逼,貌似最后都是很侥幸的通过万能的Google给汤坑汤沟去了,说来也是惭愧,本来很简单的事情,却需要花费一些不必要的时间,所以,当时就想如果有时间,一定要好好研究下flutter的原理,我们接入flutter,flutter到底是如何启动的,启动的过程中他做了一些什么,假如以后需要做性能优化,我们该从何处入手。
首先,这里有必要介绍一下我们接入flutter的方式,是的采用的是这种方式,相当于是直接字创建一个FlutterView
,随后把这个FlutterView塞给一个Fragment即可。
我们这里采用Fragment的原因是,复用性,比如首页Tab页面需要替换为Flutter页面,我们可以直接上这个Fragment,然后,比较独立的业务需要使用activity的话,也是可以直接嵌入这个有FlutterView的Fragment的。暂且不说这种做法的缺点,我们先来了解一下FlutterView是怎么一步一步创建出来的。
当从native页打开一个flutter页,比如,你的首页的第三个tab是flutter,且启动App,进入首页,当你点击切换到第三个tab的时候,那么,此时就会触发这个创建FlutterView的流程。
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已经准备启动。
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了。
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进行了关联
@UiThread
public void attachToNative(boolean isBackgroundView) {
this.ensureRunningOnMainThread();
this.ensureNotAttachedToNative();
this.nativePlatformViewId = this.nativeAttach(this, isBackgroundView);
}
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的桥梁也不为过。
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上面的触摸事件是如何处理的,在这里也有了呈现,那就是,触摸点击等事件被AndroidKeyProcessor
,AndroidTouchProcessor
包装成了元数据发送给了flutter层去处理。
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接入层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 删除。