在調研 Flutter 動態化方案的時候,須要瞭解 Flutter 加載 dart 產物的流程,閱讀了一部分源碼,順便也讀了初始化相關的代碼。因而梳理了一遍 Flutter 的初始化流程java
flutter的源碼下載地址在 github 上能夠找到,具體地址: github-flutter/engineandroid
先從 Android 的入口開始看c++
在 FlutterAppliation
的 onCreate
中調用了git
FlutterMain.startInitialization(this);
複製代碼
跟進去咱們會看到調用了 startInitialization
方法,最後會順序調用這幾個方法github
initConfig(applicationContext);
initAot(applicationContext);
initResources(applicationContext);
複製代碼
咱們查看 initResources
方法如圖shell
這裏咱們能夠看到實際加載了assets裏面的flutter資源。而且會把資源 copy 到本地的路徑。這裏不作深究。FlutterMan
的初始化基本包括了性能優化
3 個部分app
繼續看 Flutter
的 View
的初始化:ide
以 FlutterActivity
爲例,在 onCreate
中會調用到 FlutterActivityDelegate
的對應方法,最終調用 FlutterView
的 runFromBundle
方法函數
public void runFromBundle(FlutterRunArguments args) {
this.assertAttached();
this.preRun();
this.mNativeView.runFromBundle(args);
this.postRun();
}
複製代碼
跟蹤這段代碼,會調用 FlutterNativeView
的 nativeRunBundleAndSnapshotFromLibrary
方法。
這裏會繼續進行 jni
層的調用,查看 platform_view_android_jni.cc
{
.name = "nativeRunBundleAndSnapshotFromLibrary",
.signature = "(J[Ljava/lang/String; Ljava/lang/String;"
"Ljava/lang/String;Landroid/content/res/AssetManager;)V",
.fnPtr = reinterpret_cast<void*> (shell::RunBundleAndSnapshotFromLibrary),
},
複製代碼
查看 RunBundleAndSnapshotFromLibrary
,這裏刪除了一些咱們不關心的邏輯
static void RunBundleAndSnapshotFromLibrary(JNIEnv* env,
jobject jcaller,
jlong shell_holder,
jobjectArray jbundlepaths,
jstring jEntrypoint,
jstring jLibraryUrl,
jobject jAssetManager) {
auto asset_manager = std::make_shared<blink::AssetManager>();
for (const auto& bundlepath :
fml::jni::StringArrayToVector(env, jbundlepaths)) {
const auto file_ext_index = bundlepath.rfind(".");
if (bundlepath.substr(file_ext_index) == ".zip") {
asset_manager->PushBack(
std::make_unique<blink::ZipAssetStore>(bundlepath));
} else {
asset_manager->PushBack(
std::make_unique<blink::DirectoryAssetBundle>(fml::OpenDirectory(
bundlepath.c_str(), false, fml::FilePermission::kRead)));
const auto last_slash_index = bundlepath.rfind("/", bundlepath.size());
if (last_slash_index != std::string::npos) {
auto apk_asset_dir = bundlepath.substr(
last_slash_index + 1, bundlepath.size() - last_slash_index);
asset_manager->PushBack(std::make_unique<blink::APKAssetProvider>(
env, // jni environment
jAssetManager, // asset manager
std::move(apk_asset_dir)) // apk asset dir
);
}
}
}
auto isolate_configuration = CreateIsolateConfiguration(*asset_manager);
RunConfiguration config(std::move(isolate_configuration),
std::move(asset_manager));
ANDROID_SHELL_HOLDER->Launch(std::move(config));
複製代碼
首先會對資源路徑進行處理 會分爲 zip
包或者文件夾進行分別處理。最終會調用常量ANDROID_SHELL_HOLDER
的 Launch
函數.
最終走到 engine
的 Run
函數。
這裏有 2 個函數比較重要,先是 IsolateConfiguration::PrepareIsolate
, 而後是 RunFromLibrary
或者 Run
函數
跟到 PrepareAndLaunchIsolate
函數,查看源碼
bool IsolateConfiguration::PrepareIsolate(blink::DartIsolate& isolate) {
if (isolate.GetPhase() != blink::DartIsolate::Phase::LibrariesSetup) {
FML_DLOG(ERROR)
<< "Isolate was in incorrect phase to be prepared for running.";
return false;
}
return DoPrepareIsolate(isolate);
}
複製代碼
而有 DoPrepareIsolate
函數的類 Configuration
類有3個
他們分別會調用 DartIsolate
的
這2個方法的一個,能夠看到這裏的prepare
操做分紅了 預先加載的代碼 和 從內核獲取 2種
至於 RunFromLibrary
函數和 Run
函數
咱們能看到他們最終都會調用 dart:isolate
和 _startMainIsolate
的邏輯:
Dart_Handle isolate_lib = Dart_LookupLibrary(tonic::ToDart("dart:isolate"));
if (tonic::LogIfError(Dart_Invoke(
isolate_lib, tonic::ToDart("_startMainIsolate"),
sizeof(isolate_args) / sizeof(isolate_args[0]), isolate_args))) {
return false;
}
複製代碼
這裏說明咱們正在執行調用 Dart
的入口方法。而 Run
和 RunFromLibrary
的區別,則是若是咱們傳入了 entrypoint
參數去進行 Flutter 的 bundle 初始化的時候,則會去加載咱們制定的 library。
到這裏, Flutter 的初始化流程就就簡單的分析了一遍。大體能夠總結成三個部分
初始化的邏輯比較複雜,對後續一些初始化相關的性能優化應該也會有不小的啓發。FlutterMain
中對資源的處理和寫入本地的邏輯也給 Android 端研究 Flutter 動態化提供了基礎。
不少 bundle 加載和後續初始化的邏輯也尚未徹底弄清楚和深刻研究。有興趣的朋友能夠一塊兒研究,共同探討。