關於Flutter初始化,我必須告訴你的是...(乾貨)
引言
最近在做效能最佳化的時候發現,在混合棧開發中,第一次啟動Flutter頁面的耗時總會是第二次啟動Flutter頁面耗時的兩倍左右,這樣給人感覺很不好。分析發現第一次啟動Flutter頁面會做一些初始化工作,藉此,我梳理了下Flutter的初始化流程。
Flutter初始化主要分四部分,FlutterMain初始化、FlutterNativeView的初始化、FlutterView初始化和Flutter Bundle初始化。 我們先看下Flutter初始化的時序圖,來整體把握下Flutter初始化的一般流程:
具體分析
FlutterMain初始化
這部分初始化工作是由Application.onCreate方法中呼叫開始的,在Application建立的時候就會初始化完成,不會影響Flutter頁面的第一次啟動,所以這裡只是做一個簡單分析。
從FlutterMain.startInitialization方法程式碼中可以輕易看出來,初始化主要分四部分。
前面三部分比較類似,分別是初始化配置資訊、初始化AOT編譯和初始化資源,最後一部分則是載入Flutter的Native環境。
這部分感興趣的同學可以看下FlutterMain.java原始碼,邏輯還是比較清晰的。
public static void startInitialization(Context applicationContext, Settings settings) { // other codes ... initConfig(applicationContext); initAot(applicationContext); initResources(applicationContext); System.loadLibrary("flutter"); // other codes ... }
FlutterNativeView初始化
先用一個圖來展現FlutterNativeView建構函式的呼叫棧:
從上圖的呼叫棧中我們知道FlutterNativeView的初始化主要做了些什麼,我們再從原始碼角度較為深入的瞭解下:
FlutterNativeView的建構函式最終主要呼叫了一個nativeAttach方法。到這裡就需要分析引擎層程式碼了,我們可以在JNI檔案中找到對應的jni方法呼叫。(具體檔案為platform_view_android_jni.cc)
static const JNINativeMethod native_view_methods[] = { { .name = "nativeAttach", .signature = "(Lio/flutter/view/FlutterNativeView;)J", .fnPtr = reinterpret_cast<void*>(&shell::Attach), }, // other codes ... };
從程式碼中很容易看出FlutterNativeView.attach方法最終呼叫了shell::Attach方法,而shell::Attach方法主要做了兩件事:
1. 建立PlatformViewAndroid。
2. 呼叫PlatformViewAndroid::Attach。
static jlong Attach(JNIEnv* env, jclass clazz, jobject flutterView) { auto view = new PlatformViewAndroid(); // other codes ... view->Attach(); // other codes ... }
那我們再分析下PlatformViewAndroid的建構函式和Attach方法都做了些什麼呢?
PlatformViewAndroid::PlatformViewAndroid() : PlatformView(std::make_unique<NullRasterizer>()), android_surface_(InitializePlatformSurface()) {} void PlatformViewAndroid::Attach() { CreateEngine(); // Eagerly setup the IO thread context. We have already setup the surface. SetupResourceContextOnIOThread(); UpdateThreadPriorities();
其中:
1. PlatformViewAndroid的建構函式主要是呼叫了InitializePlatformSurface方法,這個方法主要是初始化了Surface,其中Surface有Vulkan、OpenGL和Software三種型別的區別。
2. PlatformViewAndroid::Attach方法這裡主要呼叫三個方法:CreateEngine、SetupResourceContextOnIOThread和UpdateThreadPriorities。
2.1 CreateEngine比較好理解,建立Engine,這裡會重新建立一個Engine物件。
2.2 SetupResourceContextOnIOThread是在IO執行緒去準備資源的上下文邏輯。
2.3 UpdateThreadPriorities是設定執行緒優先順序,這設定GPU執行緒優先順序為-2,UI執行緒優先順序為-1。
FlutterView初始化
FlutterView的初始化就是純粹的Android層啦,所以相對比較簡單。分析FlutterView.java的建構函式就會發現,整個FlutterView的初始化在確保FlutterNativeView的建立成功和一些必要的view設定之外,主要做了兩件事:
1. 註冊SurfaceHolder監聽,其中surfaceCreated回撥會作為Flutter的第一幀回撥使用。
2. 初始化了Flutter系統需要用到的一系列橋接方法。例如:localization、navigation、keyevent、system、settings、platform、textinput。
FlutterView初始化流程主要如下圖所示:
Flutter Bundle初始化
Flutter Bundle的初始化是由呼叫FlutterActivityDelegate.runFlutterBundle開始的,先用一張圖來說明下runFlutterBundle方法的呼叫棧:
我們再從原始碼角度較為深入瞭解下:
FlutterActivity的onCreate方法在執行完FlutterActivityDelegate的onCreate方法之後會呼叫它的runFlutterBundle方法。runFlutterBundle程式碼如下:
public void runFlutterBundle(){ // other codes ... String appBundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext()); if (appBundlePath != null) { flutterView.runFromBundle(appBundlePath, null, "main", reuseIsolate); } }
很明顯,這個runFlutterBundle並沒有做太多事情,而且直接呼叫了FlutterView.runFromBundle方法。而後兜兜轉轉最後會呼叫到PlatformViewAndroid::RunBundleAndSnapshot方法。
void PlatformViewAndroid::RunBundleAndSnapshot(JNIEnv* env, std::string bundle_path, std::string snapshot_override, std::string entrypoint, bool reuse_runtime_controller, jobject assetManager) { // other codes ... blink::Threads::UI()->PostTask( [engine = engine_->GetWeakPtr(), asset_provider = std::move(asset_provider), bundle_path = std::move(bundle_path), entrypoint = std::move(entrypoint), reuse_runtime_controller = reuse_runtime_controller] { if (engine) engine->RunBundleWithAssets( std::move(asset_provider), std::move(bundle_path), std::move(entrypoint), reuse_runtime_controller); }); }
PlatformViewAndroid::RunBundleAndSnapshot在UI執行緒中呼叫Engine::RunBundleWithAssets,最終呼叫Engine::DoRunBundle。
DoRunBundle方法最後只會呼叫RunFromPrecompiledSnapshot、RunFromKernel和RunFromScriptSnapshot三個方法中的一個。而這三個方法最終都會呼叫SendStartMessage方法。
bool DartController::SendStartMessage(Dart_Handle root_library, const std::string& entrypoint) { // other codes ... // Get the closure of main(). Dart_Handle main_closure = Dart_GetClosure( root_library, Dart_NewStringFromCString(entrypoint.c_str())); // other codes ... // Grab the 'dart:isolate' library. Dart_Handle isolate_lib = Dart_LookupLibrary(ToDart("dart:isolate")); DART_CHECK_VALID(isolate_lib); // Send the start message containing the entry point by calling // _startMainIsolate in dart:isolate. const intptr_t kNumIsolateArgs = 2; Dart_Handle isolate_args[kNumIsolateArgs]; isolate_args[0] = main_closure; isolate_args[1] = Dart_Null(); Dart_Handle result = Dart_Invoke(isolate_lib, ToDart("_startMainIsolate"), kNumIsolateArgs, isolate_args); return LogIfError(result); }
而SendStartMessage方法主要做了三件事:
1. 獲取Flutter入口方法(例如main方法)的closure。
2. 獲取FlutterLibrary。
3. 傳送訊息來呼叫Flutter的入口方法。
總結
本次主要分析了下FlutterActivity的onCreate方法中的Flutter初始化部分邏輯,很明顯會發現主要耗時在FlutterNativeView、FlutterView和Flutter Bundle的初始化這三塊,將這三部分的初始化工作前置就可以比較容易的解決引言中提出的問題。經測試發現,這樣改動之後,Flutter頁面第一次啟動時長和後面幾次啟動時長差不多一樣了。
對於FlutterMain.startInitialization的初始化邏輯、SendStartMessage傳送的訊息如何最終呼叫Flutter中的入口方法邏輯沒有進一步深入分析,這些內容後續再繼續分析撰文分享。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69900359/viewspace-2222023/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 我必須得告訴大家的MySQL優化原理MySql優化
- 關於自定義轉場動畫,我都告訴你。動畫
- 關於Flutter 您必須知道的知識點!!!Flutter
- 告訴你 Redis 是一個牛逼貨Redis
- 關於Http協議,你必須要知道的HTTP協議
- 關於機器學習你必須瞭解的十個真相機器學習
- 關於NoSQL,你必須知道的九件事SQL
- Excel中關於日期的所有秘密,給我1秒鐘悄悄告訴你!Excel
- 告訴你,我是一個與眾不同的密碼密碼
- 關於Web前端,你必須瞭解的發展方向Web前端
- 關於JVM,你必須知道的這些知識點JVM
- 關於 PHP 7 你必須知道的五件事PHP
- 一分鐘乾貨告訴你區塊鏈究竟是啥?區塊鏈
- Java,你告訴我 fail-fast 是什麼鬼?JavaAIAST
- 超長乾貨:親歷者告訴你如何做出微軟風格的PPT?微軟
- 關於LLVM,這些東西你必須知道!LVM
- 關於索引必須知道的知識索引
- 關於ES模組你必須要知道的一些禁忌(一)
- 關於Mysql事務,你必須知道的幾個知識點!MySql
- Flutter必須理解Widget、Element、RenderObject的關係(二)FlutterObject
- 你必須掌握在Flutter中新增資原始檔Flutter
- 乾貨HTTP/2.0,騙你是小狗HTTP
- 思考是必須的
- 關於PaaS的純乾貨總結
- 關於Linux作業系統,這些你必須知道!Linux作業系統
- 告訴我你的廚房有什麼智慧物件物件
- 關於時序資料庫,你必須要知道的那些事兒!資料庫
- 告訴你關於結對程式設計的7件事程式設計
- 關於iPad Pro,蘋果沒告訴你的8件事iPad蘋果
- 沒人告訴你關於z-index的一些事Index
- 告訴你製作直播平臺都需要什麼硬體和軟體的乾貨文
- 關於Linux Shell的訊號trap功能你必須知道的細節Linux
- 誰告訴你 Flutter 會幹掉原生開發?Flutter
- 你必須知道的 SmartSql !SQL
- 遊戲是如何告訴"你快要死了”?遊戲
- 關於響應式佈局,你必須要知道關於響應式佈局的幾件事
- 別告訴我你懂陣列(2)薦陣列
- 關於密碼測評,你必須瞭解的10個基本問題密碼