關於Flutter你要知道的可能都在這兒

ExileZhu發表於2019-11-10

先從一張架構預覽圖說起吧:

Flutter架構
Flutter架構分Framework/Engine/Embedder三層:

Framework

Framework層是純dart語言寫的,它實現了一套用於處理Animation、Painting和Gestures的基礎功能,並基於Painting封裝了一套widget用來抹平跨平臺UI展示的差異 以及提供了一套減少由於UI改變控制元件重複建立銷燬帶來的效能消耗

Embedder層

什麼是Embedder ? 個人理解是flutter想要在不同的平臺上執行就要在不同的平臺上建立容器詳情點選這裡 比如在Android上就要建立FlutterActivity/FlutterFragment/FlutterView 通過檢視原始碼可以發現Embedder層主要負責 Engine的初始化 執行緒設定 渲染元件的設定 資原始檔的初始化

Engine層

Engine層是由C++編寫是flutter的核心負責初始化,Dart虛擬機器管理,Flutter底層的通訊和事件機制

Flutter自提供widget和Sika渲染引擎抹平平臺差異

####Embedder如何和Engine建立關係 跟蹤FlutterEmbedder層程式碼中會發現不論是FlutterActivity還是FlutterFragment最終Flutter的容器還是FlutterView,而FlutterView只不過繼承了SurfaceView

/**
 * An Android view containing a Flutter app.
 */
public class FlutterView extends SurfaceView implements BinaryMessenger, TextureRegistry {
//...
        if (nativeView == null) {
            mNativeView = new FlutterNativeView(activity.getApplicationContext());
        } else {
            mNativeView = nativeView;
        }

        dartExecutor = mNativeView.getDartExecutor();
        flutterRenderer = new FlutterRenderer(mNativeView.getFlutterJNI());
       //...
        mNativeView.attachViewAndActivity(this, activity);

        mSurfaceCallback = new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                assertAttached();
                mNativeView.getFlutterJNI().onSurfaceCreated(holder.getSurface());
            }

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

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                assertAttached();
                mNativeView.getFlutterJNI().onSurfaceDestroyed();
            }
        };
        getHolder().addCallback(mSurfaceCallback);
// ...   
}
複製程式碼

surface的改變會通知nativeview,FlutterView好像就是nativeview的裝飾器,那麼讓我們進入FlutterNativeView看看:

public FlutterNativeView(@NonNull Context context, boolean isBackgroundView) {
  //...
        mFlutterJNI = new FlutterJNI();
  //...構造後直接呼叫attach方法呼叫和Engine層建立關係
         attach(this, isBackgroundView);
    }

FlutterJNI getFlutterJNI() {
        return mFlutterJNI;
    }

 private void attach(FlutterNativeView view, boolean isBackgroundView) {
        mFlutterJNI.attachToNative(isBackgroundView);
        dartExecutor.onAttachedToJNI();
    }
複製程式碼

由此可以看出Navtive是通過FlutterJNI和JNI層關聯並初始化Engine ####關聯並Engine初始化 attachToNative 的程式碼位置在這裡:/flutter/shell/platform/android/platform_view_android_jni.cc

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;
  }
}

複製程式碼

reinterpret_cast是C++裡的強制型別轉換符。這裡reinterpret_cast()方法將該AndroidShellHolder物件指標強制轉化為long型別的值並返回java層儲存。 那就從AndroidShellHolder分析:/flutter/shell/platform/android/android_shell_holder.cc

step1: 建立四個執行緒 並把當前執行緒設定platform thread
  // The current thread will be used as the platform thread. Ensure that the
  // message loop is initialized.
  fml::MessageLoop::EnsureInitializedForCurrentThread();
  fml::RefPtr<fml::TaskRunner> gpu_runner;
  fml::RefPtr<fml::TaskRunner> ui_runner;
  fml::RefPtr<fml::TaskRunner> io_runner;
  fml::RefPtr<fml::TaskRunner> platform_runner =
      fml::MessageLoop::GetCurrent().GetTaskRunner();
  if (is_background_view) {
    ...
  } else {
    gpu_runner = thread_host_.gpu_thread->GetTaskRunner();
    ui_runner = thread_host_.ui_thread->GetTaskRunner();
    io_runner = thread_host_.io_thread->GetTaskRunner();
  }
  blink::TaskRunners task_runners(thread_label,     // label
                                  platform_runner,  // platform
                                  gpu_runner,       // gpu
                                  ui_runner,        // ui
                                  io_runner         // io
  );
複製程式碼
step2:
shell_ =
      Shell::Create(task_runners,             // task runners
                    settings_,                // settings
                    on_create_platform_view,  // platform view create callback
                    on_create_rasterizer      // rasterizer create callback
      );
複製程式碼

/flutter/shell/common/shell.cc 如果編譯Engine不方便方便可在github直接檢視

std::unique_ptr<Shell> Shell::Create(
    TaskRunners task_runners,
    Settings settings,
    Shell::CreateCallback<PlatformView> on_create_platform_view,
    Shell::CreateCallback<Rasterizer> on_create_rasterizer) {
  PerformInitializationTasks(settings);
  PersistentCache::SetCacheSkSL(settings.cache_sksl);

  TRACE_EVENT0("flutter", "Shell::Create");

  auto vm = DartVMRef::Create(settings);
  FML_CHECK(vm) << "Must be able to initialize the VM.";

  auto vm_data = vm->GetVMData();

  return Shell::Create(std::move(task_runners),             //
                       std::move(settings),                 //
                       vm_data->GetIsolateSnapshot(),       // isolate snapshot
                       DartSnapshot::Empty(),               // shared snapshot
                       std::move(on_create_platform_view),  //
                       std::move(on_create_rasterizer),     //
                       std::move(vm)                        //
  );
}
//...
std::unique_ptr<Shell> Shell::Create(
    TaskRunners task_runners,
    Settings settings,
    fml::RefPtr<const DartSnapshot> isolate_snapshot,
    fml::RefPtr<const DartSnapshot> shared_snapshot,
    Shell::CreateCallback<PlatformView> on_create_platform_view,
    Shell::CreateCallback<Rasterizer> on_create_rasterizer,
    DartVMRef vm) {
//...
  shell = CreateShellOnPlatformThread(std::move(vm),
                                            std::move(task_runners),      //
                                            settings,                     //
                                            std::move(isolate_snapshot),  //
                                            std::move(shared_snapshot),   //
                                            on_create_platform_view,      //
                                            on_create_rasterizer          //
        );
//...
}
複製程式碼

1根據setting執行了初始化Task 2建立了DartVM並將其傳入Shell的Create方法中 在這裡找到Shell真正的Create方法CreateShellOnPlatformThread

 // Create the rasterizer on the GPU thread.
 // Create the platform view on the platform thread (this thread).
 // Ask the platform view for the vsync waiter. This will be used by the engine
auto vsync_waiter = platform_view->CreateVSyncWaiter();
// Create the IO manager on the IO thread.
// Create the engine on the UI thread.
複製程式碼

根據程式碼註釋可知: 1 在GPUThread建立rasterizer 2 在platform thread(APP主執行緒) 建立PlatformView 並獲取其VSyncWaiter傳遞給Engine 3 在UIThread建立Engine 然後通過shell的Setup方法呼叫將platform_view、io_manager、rasterizer和engine四個unique_ptr儲存到Shell物件中交由Shell物件管理並將Create的結果shell返回給AndroidShellHolder 由此Embedder層和Engine層就建立了聯絡 並初始化了 四個執行緒 一個DartVM 和一個Engine。 這裡畫了一個圖總結一下:

image.png

####Flutter與執行緒 Flutter Engine自己不建立, 管理執行緒。Flutter Engine執行緒的建立和管理是由embedder負責的

Embeder提供四個Task Runner, 每個Task Runner負責不同的任務,Flutter Engine不在乎Task Runner具體跑在哪個執行緒,但是它需要執行緒配置在整一個生命週期裡面保持穩定。也就是說一個Runner最好始終保持在同一執行緒執行

Platform Task Runner

根據程式碼在初始化Engine的時候,將當前執行緒設定為PlatformTaskRunner的Thread,是Flutter和Native互動的橋樑,而且官方要求所有和Flutter的互動必須發生在PlatformThread中,因為FlutterEngine中有很多模組是非執行緒安全的,所以如果在其他執行緒呼叫Flutter engine會出現無法預期的異常 然是值得注意的是,PlatformThread阻塞並不會直接導致Flutter應用卡頓,因為Flutter渲染的核心在UIThread中

UI Task Runner

前文中Embedder和Engine關聯後,Engine就開始著手執行Dart root isolate程式碼 ,Root Isolate負責建立管理的Layer Tree最終決定什麼內容要繪製到螢幕上。因此這個執行緒的過載會直接導致卡頓掉幀。

GPU Task Runner

GPU Task Runner中的模組負責將Layer Tree提供的資訊轉化為實際的GPU指令。GPU Task Runner同時也負責配置管理每一幀繪製所需要的GPU資源,這包括平臺Framebuffer的建立,Surface生命週期管理,保證Texture和Buffers在繪製的時候是可用的。 GPU Runner可以導致UI Runner的幀排程的延遲,GPU Runner的過載會導致Flutter應用的卡頓。一般來說使用者沒有機會向GPU Runner直接提交任務,因為平臺和Dart程式碼都無法跑進GPU Runner。但是Embeder還是可以向GPU Runner提交任務的。因此建議為每一個Engine例項都新建一個專用的GPU Runner執行緒。

IO Task Runner

主要功能是從圖片儲存(比如磁碟)中讀取壓縮的圖片格式,將圖片資料進行處理為GPU Runner的渲染做好準備。

總結:

Platform Thread GPU Thread UI Thread IO Thread
Flutter Engine的介面呼叫 執行裝置GPU的指令 執行Dart root isolate程式碼 讀取並處理圖片資料

isolate

isolate是隔離的,每個isolate有自己的記憶體和單執行緒執行的實體. isolate之間不互相共享記憶體,且獨立GC。 isolate中的程式碼是順序執行的,且是單執行緒,所以不存在資源競爭和變數狀態同步的問題,也就不需要鎖。Dart中的併發都是多個isolate並行實現的

由於isolate不共享記憶體,所以isolate之間不能直接互相通訊,只能通過Port進行通訊,而且是非同步的

Flutter Engine Runners與Dart Isolate

Dart的Isolate是Dart虛擬機器自己管理的,Flutter Engine無法直接訪問。Root Isolate通過Dart的C++呼叫能力把UI渲染相關的任務提交到UI Runner執行, 這樣就可以跟Flutter Engine相關模組進行互動,Flutter UI相關的任務也被提交到UI Runner也可以相應的給Isolate一些事件通知, Dart isolate跟Flutter Runner是相互獨立的,它們通過任務排程機制相互協作

DalvikVM與DartVM

DartVM的記憶體管理

準確的說DartVM和JVM的GC機制相似 使用分年代收集的方式,將記憶體分為新生代和老年代:

新生代

初次分配的物件都位於新生代中,該區域主要是存放記憶體較小並且生命週期較短的物件,比如區域性變數。新生代會頻繁執行記憶體回收(GC),回收採用“複製-清除”演算法,將記憶體分為兩塊,執行時每次只使用其中的一塊,另一塊備用。當發生GC時,將當前使用的記憶體塊中存活的物件拷貝到備用記憶體塊中,然後清除當前使用記憶體塊,最後,交換兩塊記憶體的角色。

年輕代GC

老年代

老年代GC採用的是標記-清理的方式而且在標記的時候,該執行緒中記憶體區域是處於不可修改的狀態,類似於JVM中stop the world,但是由於dart優秀的schedule機制和老年代GC頻率很低的原因,基本上不會出現這個問題。

schedule

在檢視engine原始碼的過程中我們看到,dartVM被作為一個變數傳入了engine,為了最小化GC對APP和UI的效能影響,當FlutterApp處於空閒/無使用者互動/後臺的情況下,engine呼叫DartVM觸發GC

isolate

記得在JVM中Thread是執行操作的最小單元,Dart區別於JVM的是: isolate有自己獨立的堆疊 isolate之間記憶體不共享 isolate之間GC不會相互影響 isolate之間不能直接相互通訊,只能通過Port進行非同步通訊 整個Flutter Framework Widget的渲染過程都執行在一個isolate中

DartVM區別於DalvikVM
由此可見Dart天生不存在資料競爭和變數不同步的問,或者說Dart是單執行緒的,可是在移動端,APP大多數時間是處於等待的狀態:等待網路請求結果,等待使用者操作等因此Flutter選擇Dart作為開發語言也有一定的道理。

相關文章