flutter單引擎方案

原文連接:cloud.tencent.com/developer/a…java

假設有兩個模塊,FlutterA,FlutterB,咱們利用io.flutter.embedding.android.FlutterFragment下面的接入方式來接入flutter的話,下圖,展現的是FlutterA模塊,拉起一個獨立的FLutterB模塊,此時,會依照順序發生下面的生命週期函數。android

這裏須要注意的點有:shell

  1. FlutterA頁面在拉起FlutterB頁面以後,沒有執行onDestoryView方法,也就是說View還在。
  2. FLutterA頁面拉起FlutterB以後,一直到FlutterB徹底可見以後,才執行了onStop方法。
  3. FlutterB回退到FlutterA以後,最終走到了onDesctoryVIew

FLutterA徹底拉起FlutterB以後,爲何內存中只有一個引擎了。

首先,咱們知道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

FlutterB回退到FlutterA,FlutterA的狀態爲何能夠繼續保存

咱們注意到生命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

首先看看Flutter引擎的初始化
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又能夠刷新了

相關文章
相關標籤/搜索