原文连接:cloud.tencent.com/developer/a…java
假设有两个模块,FlutterA,FlutterB,咱们利用io.flutter.embedding.android.FlutterFragment
下面的接入方式来接入flutter的话,下图,展现的是FlutterA模块,拉起一个独立的FLutterB模块,此时,会依照顺序发生下面的生命周期函数。android
这里须要注意的点有:shell
FlutterA
页面在拉起FlutterB页面以后,没有执行onDestoryView方法,也就是说View还在。首先,咱们知道FLutterA必定会走到其生命周期函数onStop,进而会触发FlutterActivityAndFragmentDelegate的onStop方法。bash
//FlutterActivityAndFragmentDelegate
void onStop() {
Log.v("FlutterActivityAndFragmentDelegate", "onStop()");
this.ensureAlive();
this.flutterEngine.getLifecycleChannel().appIsPaused();
this.flutterView.detachFromFlutterEngine();
}复制代码
咱们注意到,这里调用了flutterView的detachFromFlutterEngine方法。app
public void detachFromFlutterEngine() {
Log.d("FlutterView", "Detaching from a FlutterEngine: " + this.flutterEngine);
if (!this.isAttachedToFlutterEngine()) {
Log.d("FlutterView", "Not attached to an engine. Doing nothing.");
} else {
Iterator var1 = this.flutterEngineAttachmentListeners.iterator();
while(var1.hasNext()) {
FlutterView.FlutterEngineAttachmentListener listener = (FlutterView.FlutterEngineAttachmentListener)var1.next();
listener.onFlutterEngineDetachedFromFlutterView();
}
this.flutterEngine.getPlatformViewsController().detachAccessibiltyBridge();
this.accessibilityBridge.release();
this.accessibilityBridge = null;
this.textInputPlugin.getInputMethodManager().restartInput(this);
this.textInputPlugin.destroy();
FlutterRenderer flutterRenderer = this.flutterEngine.getRenderer();
this.didRenderFirstFrame = false;
flutterRenderer.removeOnFirstFrameRenderedListener(this.onFirstFrameRenderedListener);
flutterRenderer.detachFromRenderSurface();
this.flutterEngine = null;
}
}复制代码
咱们最终看到,调用了this.flutterEngine = null;,引擎被释放了,因此,只要是这种模式,你不管开多少个Flutter模块,最后都只会有一个引擎。ide
咱们注意到生命FlutterA会来时会执行周期函数onStart,它又会走到FlutterActivityAndFragmentDelegate的onStart。函数
void onStart() {
Log.v("FlutterActivityAndFragmentDelegate", "onStart()");
this.ensureAlive();
(new Handler()).post(new Runnable() {
public void run() {
Log.v("FlutterActivityAndFragmentDelegate", "Attaching FlutterEngine to FlutterView.");
FlutterActivityAndFragmentDelegate.this.flutterView.attachToFlutterEngine(FlutterActivityAndFragmentDelegate.this.flutterEngine);
FlutterActivityAndFragmentDelegate.this.doInitialFlutterViewRun();
}
});
}复制代码
咱们注意到这里又一个attachToFlutterEngine的方法,那么,它绑定的是FlutterActivityAndFragmentDelegate.this.flutterEngine,这个引擎是咱们实现的一个EngineProvider每次须要的时候new出来的。post
public class TipEngineProvider {
public static FlutterEngine obtain() {
return new FlutterEngine(IGameApplication.getIGameApplicationContext());
}
}复制代码
那既然是new出来的一个引擎,咱们不由要问了,为何回退回来,FlutterA的页面状态还能够保存,好比,列表滑动到必定的位置,打开FlutterB,在回退回来,FlutterA还保存在列表滑动到的最后位置,没变化。ui
带着这个问题,咱们只能去看看,onStart中绑定引擎,及初始化引发的过程当中,作了一些什么?this
public FlutterEngine(@NonNull Context context) {
this.flutterJNI.addEngineLifecycleListener(this.engineLifecycleListener);
this.attachToJni();
this.dartExecutor = new DartExecutor(this.flutterJNI, context.getAssets());
this.dartExecutor.onAttachedToJNI();
this.renderer = new FlutterRenderer(this.flutterJNI);
}复制代码
以上代码有省略,只保留相当重要的部分。这里咱们看到,在FlutterEngine的初始化中,flutterJNI绑定了Native,就在此时拿到了nativePlatformViewId,这里咱们先埋下一个伏笔,继续走,而后,初始化了DartExecutor,DartExecutor绑定到了flutterJNI,而后初始化了FlutterRenderer。好,这里先看到这一步,总结一下,FlutterEngine融合了不少关键的成员。
在FlutterSurfaceView中,这个类继承至SurfaceView,实现了RenderSurface接口,在经过调用FlutterRenderer的surfaceCreated方法,最终会调用到flutterJNI的surfaceCreated,最终经过调用nativeSurfaceCreated方法和nativePlatformViewId产生了关联,如下是源码部分。
//FlutterSurfaceView 中的connectSurfaceToRenderer方法
private void connectSurfaceToRenderer() {
if (this.flutterRenderer != null && this.getHolder() != null) {
this.flutterRenderer.surfaceCreated(this.getHolder().getSurface());
} else {
throw new IllegalStateException("connectSurfaceToRenderer() should only be called when flutterRenderer and getHolder() are non-null.");
}
}复制代码
//FlutterRenderer 中的surfaceCreated方法
public void surfaceCreated(@NonNull Surface surface) {
this.flutterJNI.onSurfaceCreated(surface);
}复制代码
//FlutterJNI中的onSurfaceCreated方法
@UiThread
public void onSurfaceCreated(@NonNull Surface surface) {
this.ensureRunningOnMainThread();
this.ensureAttachedToNative();
this.nativeSurfaceCreated(this.nativePlatformViewId, surface);
}复制代码
换句话说,最终flutter底层经过nativePlatformViewId将数据绘制到了原生的surface上。
咱们注意到,connectSurfaceToRenderer方法有两次调用的时机,第一次是在surface初次create的时候,还有一次是主动去掉,看代码
private FlutterSurfaceView(@NonNull Context context, @Nullable AttributeSet attrs, boolean renderTransparently) {
super(context, attrs);
this.isSurfaceAvailableForRendering = false;
this.isAttachedToFlutterRenderer = false;
this.onFirstFrameRenderedListeners = new HashSet();
this.surfaceCallback = new Callback() {
public void surfaceCreated(@NonNull SurfaceHolder holder) {
Log.v("FlutterSurfaceView", "SurfaceHolder.Callback.surfaceCreated()");
FlutterSurfaceView.this.isSurfaceAvailableForRendering = true;
if (FlutterSurfaceView.this.isAttachedToFlutterRenderer) {
FlutterSurfaceView.this.connectSurfaceToRenderer();
}
}复制代码
public void attachToRenderer(@NonNull FlutterRenderer flutterRenderer) {
Log.v("FlutterSurfaceView", "Attaching to FlutterRenderer.");
if (this.flutterRenderer != null) {
Log.v("FlutterSurfaceView", "Already connected to a FlutterRenderer. Detaching from old one and attaching to new one.");
this.flutterRenderer.detachFromRenderSurface();
}
this.flutterRenderer = flutterRenderer;
this.isAttachedToFlutterRenderer = true;
if (this.isSurfaceAvailableForRendering) {
Log.v("FlutterSurfaceView", "Surface is available for rendering. Connecting FlutterRenderer to Android surface.");
this.connectSurfaceToRenderer();
}
}复制代码
很显然,从FlutterB回到FlutterA的时候,由于FlutterA的页面还在,因此,surface还在,因此,只可能调用attachToRenderer,才可以将nativePlatformViewId与surface进行关联。那么,attachToRenderer是谁在何时调用的呢?
答案就是FlutterView的attachToFlutterEngine方法,它就是在什么周期函数onStart时调用的,那么,问题来了,surface没有变化,时决定FlutterA回来不变的惟一缘由吗?别忘记了,咱们还有一个nativePlatformViewId呢。
当咱们从新给一个FlutterEngine给FlutterView绑定时,发生了不少的过程,咱们把焦点放到nativePlatformViewId时如何从新拿到的。上面咱们已经知道是在FlutterEngine的初始化中,flutterJNI绑定了Native,就在此时拿到了nativePlatformViewId。
@UiThread
public void attachToNative(boolean isBackgroundView) {
this.ensureRunningOnMainThread();
this.ensureNotAttachedToNative();
this.nativePlatformViewId = this.nativeAttach(this, isBackgroundView);
}复制代码
这是一个native的方法
// Called By Java
static jlong AttachJNI(JNIEnv* env,
jclass clazz,
jobject flutterJNI,
jboolean is_background_view) {
fml::jni::JavaObjectWeakGlobalRef java_object(env, flutterJNI);
auto shell_holder = std::make_unique<AndroidShellHolder>(
FlutterMain::Get().GetSettings(), java_object, is_background_view);
if (shell_holder->IsValid()) {
return reinterpret_cast<jlong>(shell_holder.release());
} else {
return 0;
}
}复制代码
根据我打的日志来观察,nativePlatformViewId在FlutterB返回FlutterA以后,此时拿到的这个nativePlatformViewId与以前的那个是一个long。所以,如今的状况是。
nativePlatformViewId和surface都没有变化,仍是然来的那个,天然而然FlutterA的页面渲染出来的内容没有发生变化。
至此,咱们知道FlutterEngine,其实自己没有保存数据,它只是管理着原生与flutter通信的众多成员,起到了一个中心控制者的做用,一旦它再也不了,surface与nativePlatformViewId就断开了,surface将不会在刷新,一旦它被安装到了FlutterView上,surface与nativePlatformViewId从新创建起了关联,surface又能够刷新了。