前言
相信大家在學習Flutter的開始階段都看過Flutter的架構圖,如下
我們知道Flutter的應用層程式碼由Dart編寫,Framework層提供了一系列Widget和其它API,那麼這些Dart編寫的程式碼是如何在特定平臺上執行的呢,這就要從Flutter的啟動過程說起了,瞭解了Flutter的啟動過程,這個問題便迎刃而解。
我們通過架構圖可以看出Embedder是由特定的平臺實現,它其實就是將Flutter移植到各平臺的中間層程式碼。Embedder層是Flutter啟動的關鍵,其在應用啟動後,由平臺原生模組通過呼叫該層的API執行一系列操作,比如渲染層體系的設定、相關執行緒的建立等,最主要的是通過Embedder層初始化Flutter Engine,Engine中會建立DartVM、各種服務協議的初始化、Platform Channels的初始化等等,而後就會在DartVM中執行dart編寫的入口方法main方法。至此,Flutter模組就啟動成功了。
flutter啟動分析
安卓平臺程式碼分析
安卓平臺對應的Embedder層程式碼在engine原始碼的 /flutter/shell/platform/android/ 目錄下。
我們來根據flutter create my_app
命令建立的Flutter專案demo來分析。
flutter檔案資源準備和庫載入
首先,my_app應用啟動會先執行FlutterApplication
,我們來看下該類中的生命週期方法onCreate方法的實現
- /flutter/shell/platform/android/io/flutter/app/FlutterApplication.java
@Override
@CallSuper
public void onCreate() {
super.onCreate();
FlutterMain.startInitialization(this);
}
複製程式碼
FlutterMain類即是Embedder層的程式碼,該類的startInitialization方法實現如下
- /flutter/shell/platform/android/io/flutter/view/FlutterMain.java
public static void startInitialization(Context applicationContext) {
startInitialization(applicationContext, new Settings());
}
public static void startInitialization(Context applicationContext, Settings settings) {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("startInitialization must be called on the main thread");
}
// Do not run startInitialization more than once.
if (sSettings != null) {
return;
}
sSettings = settings;
long initStartTimestampMillis = SystemClock.uptimeMillis();
initConfig(applicationContext);
initAot(applicationContext);
initResources(applicationContext);
System.loadLibrary("flutter");
long initTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis;
nativeRecordStartTimestamp(initTimeMillis);
}
複製程式碼
由以上原始碼可知startInitialization方法需要在主執行緒中執行,該方法主要是初始化配置資訊、初始化AOT模式下的變數(Debug下是JIT模式)、資原始檔的初始化(主要是將asset目錄下的flutter相關資原始檔copy到私有目錄下)等,最後會將以上初始化所用時間通過JNI方法傳遞到c++層做記錄。
- /flutter/shell/platform/android/flutter_main.cc
static void RecordStartTimestamp(JNIEnv* env,
jclass jcaller,
jlong initTimeMillis) {
int64_t initTimeMicros =
static_cast<int64_t>(initTimeMillis) * static_cast<int64_t>(1000);
blink::engine_main_enter_ts = Dart_TimelineGetMicros() - initTimeMicros;
}
複製程式碼
關鍵java類的UML類圖
flutter執行時環境初始化
FlutterApplication執行完onCreate方法後會執行啟動頁面MainActivity的生命週期方法。我們發現MainActivity的onCreate方法中並沒有通過setContentView來設定顯示的檢視,由於MainActivity繼承了FlutterActivity並過載了父類的onCreate方法,所以我們看下FlutterActivity的onCreate方法
- /flutter/shell/platform/android/io/flutter/app/FlutterActivity.java
private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this);
private final FlutterActivityEvents eventDelegate = delegate;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
eventDelegate.onCreate(savedInstanceState);
}
複製程式碼
FlutterActivity繼承自Activity,那麼檢視的設定我們推測應該是在FlutterActivityDelegate的onCreate方法中,我們看下它的實現
- /flutter/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java
@Override
public void onCreate(Bundle savedInstanceState) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
window.addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(0x40000000);
window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI);
}
String[] args = getArgsFromIntent(activity.getIntent());
FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), args);
flutterView = viewFactory.createFlutterView(activity);
if (flutterView == null) {
FlutterNativeView nativeView = viewFactory.createFlutterNativeView();
flutterView = new FlutterView(activity, null, nativeView);
flutterView.setLayoutParams(matchParent);
activity.setContentView(flutterView);
launchView = createLaunchView();
if (launchView != null) {
addLaunchView();
}
}
if (loadIntent(activity.getIntent())) {
return;
}
String appBundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext());
if (appBundlePath != null) {
runBundle(appBundlePath);
}
}
複製程式碼
我們發現程式碼中有一句activity.setContentView(flutterView);
即為當前MainActivity設定顯示檢視,那麼flutterView是如何建立的呢,我們分析該語句之前的程式碼。
- 首先根據當前系統版本來設定沉浸式狀態列;
- 獲取開啟Activity時通過intent傳入的引數資訊;
- 執行FlutterMain的ensureInitializationComplete方法;
- 建立FlutterNativeView;
- 根據FlutterNativeView建立FlutterView;
- 將FlutterView設定為activity的內容檢視;
- 通過FlutterMain查詢appBundle所在路徑,並執行appBundle;
我們先來分析第3步驟執行FlutterMain的ensureInitializationComplete方法,先看一下具體實現
- /flutter/shell/platform/android/io/flutter/view/FlutterMain.java
public static void ensureInitializationComplete(Context applicationContext, String[] args) {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("ensureInitializationComplete must be called on the main thread");
}
...
if (sInitialized) {
return;
}
try {
sResourceExtractor.waitForCompletion();
List<String> shellArgs = new ArrayList<>();
shellArgs.add("--icu-data-file-path=" + sIcuDataPath);
...
String appBundlePath = findAppBundlePath(applicationContext);
String appStoragePath = PathUtils.getFilesDir(applicationContext);
String engineCachesPath = PathUtils.getCacheDirectory(applicationContext);
nativeInit(applicationContext, shellArgs.toArray(new String[0]),
appBundlePath, appStoragePath, engineCachesPath);
sInitialized = true;
} catch (Exception e) {
Log.e(TAG, "Flutter initialization failed.", e);
throw new RuntimeException(e);
}
}
複製程式碼
我們發現該方法也要求必須在主執行緒中執行,且只執行一次,一旦執行過會通過sInitialized變數來進行標識下次不再執行。try-catch程式碼塊中第一句sResourceExtractor.waitForCompletion()
表示要等待初始化時的資源初始化完畢後才會向下執行,否則會一直阻塞。下面會初始化一些引數配置資訊、flutter打包出的appBundle路徑、應用儲存目錄、引擎快取目錄等資訊,然後會呼叫JNI方法在c++層初始化這些資訊,JNI方法對應的c++方法如下
- /flutter/shell/platform/android/flutter_main.cc
void FlutterMain::Init(JNIEnv* env,
jclass clazz,
jobject context,
jobjectArray jargs,
jstring bundlePath,
jstring appStoragePath,
jstring engineCachesPath) {
std::vector<std::string> args;
args.push_back("flutter");
for (auto& arg : fml::jni::StringArrayToVector(env, jargs)) {
args.push_back(std::move(arg));
}
auto command_line = fml::CommandLineFromIterators(args.begin(), args.end());
auto settings = SettingsFromCommandLine(command_line);
settings.assets_path = fml::jni::JavaStringToString(env, bundlePath);
...
g_flutter_main.reset(new FlutterMain(std::move(settings)));
}
複製程式碼
c++層會將傳入的引數儲存到settings物件中,然後根據settings物件建立FlutterMain物件並儲存到全域性靜態變數g_flutter_main
中,供後續flutter引擎初始化使用。
接著,會通過viewFactory建立FlutterView物件,viewFactory就是實現了ViewFactory介面的FlutterActivity物件,檢視其createFlutterView方法的實現發現返回null,此時就會執行if中的程式碼塊,同樣的,會通過viewFactory建立FlutterNativeView物件,我們檢視原始碼發現同樣返回null,這兩個方法在FlutterActivity中的實現如下
- /flutter/shell/platform/android/io/flutter/app/FlutterActivity.java
@Override
public FlutterView createFlutterView(Context context) {
return null;
}
@Override
public FlutterNativeView createFlutterNativeView() {
return null;
}
複製程式碼
緊接著,會開始使用FlutterView的帶引數的構造方法建立FlutterView物件,其具體實現如下
- /flutter/shell/platform/android/io/flutter/view/FlutterView.java
public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) {
super(context, attrs);
Activity activity = (Activity) getContext();
if (nativeView == null) {
mNativeView = new FlutterNativeView(activity.getApplicationContext());
} else {
mNativeView = nativeView;
}
...
mNativeView.attachViewAndActivity(this, activity);
mSurfaceCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
assertAttached();
mNativeView.getFlutterJNI().onSurfaceCreated(holder.getSurface());
}
...
};
getHolder().addCallback(mSurfaceCallback);
...
// Configure the platform plugins and flutter channels.
mFlutterLocalizationChannel = new MethodChannel(this, "flutter/localization", JSONMethodCodec.INSTANCE);
...
}
複製程式碼
這裡引數nativeView上文已經得出結論為null,要先通過FlutterNativeView的構造方法建立mNativeView物件,然後通過mNativeView呼叫attachViewAndActivity方法將FlutterView和當前的Activity做連線。
接著建立當前FlutterView(要知道它繼承自SurfaceView)的mSurfaceCallback物件並新增到當前SurfaceHolder中以監聽Surface的變化(如Surface的建立、改變和銷燬等),這些變化會執行對應的回撥方法,然後通過FlutterJNI的相關方法傳遞資料給Flutter engine層。
該方法中還會建立各種必要的平臺外掛和platform channel(用於flutter和原生之間的各種資料傳遞)。
接下來我們看下FlutterNativeView的構造方法實現
- /flutter/shell/platform/android/io/flutter/view/FlutterNativeView.java
public FlutterNativeView(Context context) {
this(context, false);
}
public FlutterNativeView(Context context, boolean isBackgroundView) {
mContext = context;
mPluginRegistry = new FlutterPluginRegistry(this, context);
mFlutterJNI = new FlutterJNI();
mFlutterJNI.setRenderSurface(new RenderSurfaceImpl());
mFlutterJNI.setPlatformMessageHandler(new PlatformMessageHandlerImpl());
mFlutterJNI.addEngineLifecycleListener(new EngineLifecycleListenerImpl());
attach(this, isBackgroundView);
assertAttached();
mMessageHandlers = new HashMap<>();
}
複製程式碼
方法中會初始化mFlutterJNI物件,該物件的作用是通過JNI方法來傳遞資訊給c++層,以便其根據不同的指令來通知flutter engine執行對應的操作。包括建立並啟動Flutter engine、當前FlutterView的Surface生命週期的通知、傳遞platform資料給dart層、回傳dart層呼叫platform層方法返回的結果資料等等。
該構造方法中有一個關鍵方法呼叫就是attach方法,其實現如下
- /flutter/shell/platform/android/io/flutter/view/FlutterNativeView.java
private void attach(FlutterNativeView view, boolean isBackgroundView) {
mFlutterJNI.attachToNative(isBackgroundView);
}
複製程式碼
通過mFlutterJNI的attachToNative方法實現java層和c++層的連線,實現如下
- /flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
@UiThread
public void attachToNative(boolean isBackgroundView) {
ensureNotAttachedToNative();
nativePlatformViewId = nativeAttach(this, isBackgroundView);
}
private native long nativeAttach(FlutterJNI flutterJNI, boolean isBackgroundView);
複製程式碼
attachToNative方法中通過呼叫JNI方法nativeAttach將當前flutterJNI物件傳遞給c++層(後續一些dart層呼叫java層的方法就是通過該flutterJNI物件呼叫對應的方法實現的,比如上一篇分享的platform channel就使用到了),得到c++層返回的nativePlatformViewId(該值非常重要,是c++層AndroidShellHolder的物件指標值,後續會通過該值呼叫一系列c++層的方法執行操作)並將其儲存以供後續使用。
這個階段關鍵java類的UML類圖
接著看下nativeAttach方法在c++中的實現,該方法非常重要,一系列Flutter engine的初始化就是在這裡做的。
- /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;
}
}
複製程式碼
我們發現該方法中通過之前初始化的儲存在g_flutter_main物件中的settings值和傳入的java物件flutterJNI建立std::unique_ptr物件(該物件通過指標佔有並管理AndroidShellHolder物件),該物件有效的情況下會呼叫release方法返回其管理物件的指標並釋放物件的所有權,reinterpret_cast()方法將該AndroidShellHolder物件指標強制轉化為long型別的值並返回java層儲存。
接下來我們通過AndroidShellHolder建構函式的實現來分析建立物件時都做了哪些操作
- /flutter/shell/platform/android/android_shell_holder.cc
AndroidShellHolder::AndroidShellHolder(
blink::Settings settings,
fml::jni::JavaObjectWeakGlobalRef java_object,
bool is_background_view)
: settings_(std::move(settings)), java_object_(java_object) {
static size_t shell_count = 1;
auto thread_label = std::to_string(shell_count++);
...
if (is_background_view) {
thread_host_ = {thread_label, ThreadHost::Type::UI};
} else {
thread_host_ = {thread_label, ThreadHost::Type::UI | ThreadHost::Type::GPU |
ThreadHost::Type::IO};
}
...
fml::WeakPtr<PlatformViewAndroid> weak_platform_view;
Shell::CreateCallback<PlatformView> on_create_platform_view =
[is_background_view, java_object, &weak_platform_view](Shell& shell) {
...
return platform_view_android;
};
Shell::CreateCallback<Rasterizer> on_create_rasterizer = [](Shell& shell) {
return std::make_unique<Rasterizer>(shell.GetTaskRunners());
};
// 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
);
shell_ =
Shell::Create(task_runners, // task runners
settings_, // settings
on_create_platform_view, // platform view create callback
on_create_rasterizer // rasterizer create callback
);
platform_view_ = weak_platform_view;
FML_DCHECK(platform_view_);
...
}
}
複製程式碼
傳入的引數is_background_view
值為false,通過前半部分程式碼我們發現會新建三個執行緒儲存到thread_host_
中,分別為gpu_thread
、ui_thread
和io_thread
,而當前的執行緒也即是platform層的UI主執行緒作為platform_thread
存在,四個執行緒分別持有一個TaskRunner物件,對應gpu_runner
、ui_runner
、io_runner
和platform_runner
,後續會通過這些TaskRunner來將一些操作放到對應的執行緒中去執行,下面大致列一下各執行緒在Flutter engine中的主要職責。
Platform Thread | GPU Thread | UI Thread | IO Thread |
---|---|---|---|
Flutter Engine的介面呼叫 | 執行裝置GPU的指令 | 執行Dart root isolate程式碼 | 讀取並處理圖片資料 |
接下來會根據四個執行緒對應的TaskRunner建立task_runners
物件,然後通過Shell::Create()
方法建立shell_
物件,看下該方法的具體實現
- /flutter/shell/common/shell.cc
std::unique_ptr<Shell> Shell::Create(
blink::TaskRunners task_runners,
blink::Settings settings,
Shell::CreateCallback<PlatformView> on_create_platform_view,
Shell::CreateCallback<Rasterizer> on_create_rasterizer) {
PerformInitializationTasks(settings);
auto vm = blink::DartVM::ForProcess(settings);
FML_CHECK(vm) << "Must be able to initialize the VM.";
return Shell::Create(std::move(task_runners), //
std::move(settings), //
vm->GetIsolateSnapshot(), //
blink::DartSnapshot::Empty(), //
std::move(on_create_platform_view), //
std::move(on_create_rasterizer) //
);
}
複製程式碼
首先執行初始化任務(包括初始化繫結到skia的跟蹤事件、skia引擎的初始化、國際化元件初始化等),接著根據settings值建立DartVM物件初始化Dart虛擬機器,最後建立std::unique_ptr物件並返回。我們先看一下DartVM的建立過程
- /flutter/runtime/dart_vm.cc
fml::RefPtr<DartVM> DartVM::ForProcess(Settings settings) {
return ForProcess(settings, nullptr, nullptr, nullptr);
}
static std::once_flag gVMInitialization;
static std::mutex gVMMutex;
static fml::RefPtr<DartVM> gVM;
fml::RefPtr<DartVM> DartVM::ForProcess(
Settings settings,
fml::RefPtr<DartSnapshot> vm_snapshot,
fml::RefPtr<DartSnapshot> isolate_snapshot,
fml::RefPtr<DartSnapshot> shared_snapshot) {
std::lock_guard<std::mutex> lock(gVMMutex);
std::call_once(gVMInitialization, [settings, //
vm_snapshot, //
isolate_snapshot, //
shared_snapshot //
]() mutable {
if (!vm_snapshot) {
vm_snapshot = DartSnapshot::VMSnapshotFromSettings(settings);
}
...
if (!isolate_snapshot) {
isolate_snapshot = DartSnapshot::IsolateSnapshotFromSettings(settings);
}
...
gVM = fml::MakeRefCounted<DartVM>(settings, //
std::move(vm_snapshot), //
std::move(isolate_snapshot), //
std::move(shared_snapshot) //
);
});
return gVM;
}
複製程式碼
此塊程式碼表示建立DartVM物件的程式碼塊只執行一次,即使從多個執行緒中被呼叫也是執行一次,保證DartVM只初始化一次,下面看下DartVM的構造方法實現
- /flutter/runtime/dart_vm.cc
DartVM::DartVM(const Settings& settings,
fml::RefPtr<DartSnapshot> vm_snapshot,
fml::RefPtr<DartSnapshot> isolate_snapshot,
fml::RefPtr<DartSnapshot> shared_snapshot)
: settings_(settings),
vm_snapshot_(std::move(vm_snapshot)),
isolate_snapshot_(std::move(isolate_snapshot)),
shared_snapshot_(std::move(shared_snapshot)),
weak_factory_(this) {
...
{
TRACE_EVENT0("flutter", "dart::bin::BootstrapDartIo");
dart::bin::BootstrapDartIo();
...
}
...
DartUI::InitForGlobal();
Dart_SetFileModifiedCallback(&DartFileModifiedCallback);
{
TRACE_EVENT0("flutter", "Dart_Initialize");
Dart_InitializeParams params = {};
...
params.create = reinterpret_cast<decltype(params.create)>(
DartIsolate::DartIsolateCreateCallback);
...
char* init_error = Dart_Initialize(¶ms);
...
}
...
}
複製程式碼
第一步,通過執行dart::bin::BootstrapDartIo()
方法引導啟動"dart:io"事件處理程式,具體方法呼叫如下
- /third_party/dart/runtime/bin/dart_io_api_impl.cc
void BootstrapDartIo() {
// Bootstrap 'dart:io' event handler.
TimerUtils::InitOnce();
EventHandler::Start();
}
複製程式碼
第二步,通過執行DartUI::InitForGlobal()
方法註冊dart的各種本地方法,這些方法的註冊類似於java的JNI方法註冊,主要用於dart層呼叫c++方法(上一篇文章中通過platform channel執行dart呼叫platform方法就使用到了Window相關的本地方法呼叫),相關原始碼如下
- /flutter/lib/ui/dart_ui.cc
void DartUI::InitForGlobal() {
if (!g_natives) {
g_natives = new tonic::DartLibraryNatives();
Canvas::RegisterNatives(g_natives);
...
FrameInfo::RegisterNatives(g_natives);
...
Window::RegisterNatives(g_natives);
// Secondary isolates do not provide UI-related APIs.
g_natives_secondary = new tonic::DartLibraryNatives();
DartRuntimeHooks::RegisterNatives(g_natives_secondary);
IsolateNameServerNatives::RegisterNatives(g_natives_secondary);
}
}
複製程式碼
這裡僅看一下Window相關的本地方法註冊
- /flutter/lib/ui/window/window.cc
void Window::RegisterNatives(tonic::DartLibraryNatives* natives) {
natives->Register({
{"Window_defaultRouteName", DefaultRouteName, 1, true},
{"Window_scheduleFrame", ScheduleFrame, 1, true},
{"Window_sendPlatformMessage", _SendPlatformMessage, 4, true},
{"Window_respondToPlatformMessage", _RespondToPlatformMessage, 3, true},
{"Window_render", Render, 2, true},
{"Window_updateSemantics", UpdateSemantics, 2, true},
{"Window_setIsolateDebugName", SetIsolateDebugName, 2, true},
});
}
複製程式碼
第三步,通過執行Dart_Initialize(¶ms)
方法來初始化Dart執行時環境,具體實現如下,緊貼出部分程式碼,其它程式碼請自行檢視原始碼。
- /third_party/dart/runtime/vm/dart_api_impl.cc
DART_EXPORT char* Dart_Initialize(Dart_InitializeParams* params) {
...
return Dart::Init(params->vm_snapshot_data, params->vm_snapshot_instructions,
params->create, params->shutdown, params->cleanup,
params->thread_exit, params->file_open, params->file_read,
params->file_write, params->file_close,
params->entropy_source, params->get_service_assets,
params->start_kernel_isolate);
}
複製程式碼
具體的初始化工作會由Dart::Init()方法實現
- /third_party/dart/runtime/vm/dart.cc
char* Dart::Init(const uint8_t* vm_isolate_snapshot,
const uint8_t* instructions_snapshot,
Dart_IsolateCreateCallback create,
Dart_IsolateShutdownCallback shutdown,
Dart_IsolateCleanupCallback cleanup,
Dart_ThreadExitCallback thread_exit,
Dart_FileOpenCallback file_open,
Dart_FileReadCallback file_read,
Dart_FileWriteCallback file_write,
Dart_FileCloseCallback file_close,
Dart_EntropySource entropy_source,
Dart_GetVMServiceAssetsArchive get_service_assets,
bool start_kernel_isolate) {
...
FrameLayout::Init();
...
OS::Init();
...
OSThread::Init();
...
Isolate::InitVM();
...
Api::Init();
...
#if defined(USING_SIMULATOR)
Simulator::Init();
#endif
...
thread_pool_ = new ThreadPool();
{
...
vm_isolate_ = Isolate::InitIsolate("vm-isolate", api_flags, is_vm_isolate);
...
Object::Init(vm_isolate_);
...
}
Api::InitHandles();
Thread::ExitIsolate(); // Unregister the VM isolate from this thread.
...
#ifndef DART_PRECOMPILED_RUNTIME
if (start_kernel_isolate) {
KernelIsolate::Run();
}
#endif // DART_PRECOMPILED_RUNTIME
return NULL;
}
複製程式碼
至此,DartVM的初始化就完成了,最後DartVM物件會返回給Shell,Shell通過如下方法建立Shell物件
- /flutter/shell/common/shell.cc
std::unique_ptr<Shell> Shell::Create(
blink::TaskRunners task_runners,
blink::Settings settings,
fml::RefPtr<blink::DartSnapshot> isolate_snapshot,
fml::RefPtr<blink::DartSnapshot> shared_snapshot,
Shell::CreateCallback<PlatformView> on_create_platform_view,
Shell::CreateCallback<Rasterizer> on_create_rasterizer) {
...
fml::AutoResetWaitableEvent latch;
std::unique_ptr<Shell> shell;
fml::TaskRunner::RunNowOrPostTask(
task_runners.GetPlatformTaskRunner(),
[&latch, //
&shell, //
task_runners = std::move(task_runners), //
settings, //
isolate_snapshot = std::move(isolate_snapshot), //
shared_snapshot = std::move(shared_snapshot), //
on_create_platform_view, //
on_create_rasterizer //
]() {
shell = CreateShellOnPlatformThread(std::move(task_runners), //
settings, //
std::move(isolate_snapshot), //
std::move(shared_snapshot), //
on_create_platform_view, //
on_create_rasterizer //
);
latch.Signal();
});
latch.Wait();
return shell;
}
複製程式碼
最終會通過Shell中的CreateShellOnPlatformThread方法在Platform Thread中建立Shell物件,由於該方法中程式碼比較多,我們分塊來進行分析,首先看一下Shell物件的建立
- /flutter/shell/common/shell.cc
auto shell = std::unique_ptr<Shell>(new Shell(task_runners, settings));
複製程式碼
通過構造方法建立shell物件,緊接著開始執行四個關鍵的程式碼塊
- /flutter/shell/common/shell.cc
// Create the platform view on the platform thread (this thread).
auto platform_view = on_create_platform_view(*shell.get());
if (!platform_view || !platform_view->GetWeakPtr()) {
return nullptr;
}
複製程式碼
- /flutter/shell/platform/android/android_shell_holder.cc
fml::WeakPtr<PlatformViewAndroid> weak_platform_view;
Shell::CreateCallback<PlatformView> on_create_platform_view =
[is_background_view, java_object, &weak_platform_view](Shell& shell) {
std::unique_ptr<PlatformViewAndroid> platform_view_android;
if (is_background_view) {
...
} else {
platform_view_android = std::make_unique<PlatformViewAndroid>(
shell, // delegate
shell.GetTaskRunners(), // task runners
java_object, // java object handle for JNI interop
shell.GetSettings()
.enable_software_rendering // use software rendering
);
}
weak_platform_view = platform_view_android->GetWeakPtr();
return platform_view_android;
};
複製程式碼
關鍵程式碼塊一:在platform thread中根據傳入的on_create_platform_view
函式建立PlatformViewAndroid物件並交由platform_view管理,如上該函式在AndroidShellHolder的建構函式中宣告。
- /flutter/shell/common/shell.cc
// Create the IO manager on the IO thread.
fml::AutoResetWaitableEvent io_latch;
std::unique_ptr<IOManager> io_manager;
auto io_task_runner = shell->GetTaskRunners().GetIOTaskRunner();
fml::TaskRunner::RunNowOrPostTask(
io_task_runner,
[&io_latch, //
&io_manager, //
&platform_view, //
io_task_runner //
]() {
io_manager = std::make_unique<IOManager>(
platform_view->CreateResourceContext(), io_task_runner);
io_latch.Signal();
});
io_latch.Wait();
複製程式碼
關鍵程式碼塊二:在IO thread中建立IOManager物件,並交由io_manager管理。
- /flutter/shell/common/shell.cc
// Create the rasterizer on the GPU thread.
fml::AutoResetWaitableEvent gpu_latch;
std::unique_ptr<Rasterizer> rasterizer;
fml::WeakPtr<blink::SnapshotDelegate> snapshot_delegate;
fml::TaskRunner::RunNowOrPostTask(
task_runners.GetGPUTaskRunner(), [&gpu_latch, //
&rasterizer, //
on_create_rasterizer, //
shell = shell.get(), //
&snapshot_delegate //
]() {
if (auto new_rasterizer = on_create_rasterizer(*shell)) {
rasterizer = std::move(new_rasterizer);
snapshot_delegate = rasterizer->GetSnapshotDelegate();
}
gpu_latch.Signal();
});
gpu_latch.Wait();
複製程式碼
- /flutter/shell/platform/android/android_shell_holder.cc
Shell::CreateCallback<Rasterizer> on_create_rasterizer = [](Shell& shell) {
return std::make_unique<Rasterizer>(shell.GetTaskRunners());
};
複製程式碼
關鍵程式碼塊三:在GPU thread中根據傳入的on_create_rasterizer
函式建立Rasterizer物件並交由rasterizer管理,如上該函式也在AndroidShellHolder的建構函式中宣告。
- /flutter/shell/common/shell.cc
// Create the engine on the UI thread.
fml::AutoResetWaitableEvent ui_latch;
std::unique_ptr<Engine> engine;
fml::TaskRunner::RunNowOrPostTask(
shell->GetTaskRunners().GetUITaskRunner(),
fml::MakeCopyable([&ui_latch, //
&engine, //
shell = shell.get(), //
isolate_snapshot = std::move(isolate_snapshot), //
shared_snapshot = std::move(shared_snapshot), //
vsync_waiter = std::move(vsync_waiter), //
snapshot_delegate = std::move(snapshot_delegate), //
io_manager = io_manager->GetWeakPtr() //
]() mutable {
const auto& task_runners = shell->GetTaskRunners();
// The animator is owned by the UI thread but it gets its vsync pulses
// from the platform.
auto animator = std::make_unique<Animator>(*shell, task_runners,
std::move(vsync_waiter));
engine = std::make_unique<Engine>(*shell, //
shell->GetDartVM(), //
std::move(isolate_snapshot), //
std::move(shared_snapshot), //
task_runners, //
shell->GetSettings(), //
std::move(animator), //
std::move(snapshot_delegate), //
std::move(io_manager) //
);
ui_latch.Signal();
}));
ui_latch.Wait();
複製程式碼
關鍵程式碼塊四:在UI thread中建立Engine物件,並交由engine管理。
最後會通過shell的Setup方法呼叫將platform_view
、io_manager
、rasterizer
和engine
四個unique_ptr
儲存到Shell物件中交由Shell物件管理
- /flutter/shell/common/shell.cc
if (!shell->Setup(std::move(platform_view), //
std::move(engine), //
std::move(rasterizer), //
std::move(io_manager)) //
) {
return nullptr;
}
複製程式碼
Shell物件通過Shell::Create()建立完成後返回給AndroidShellHolder持有。至此,Embedder層就通過Shell物件與engine層建立了連線,後續的一切操作都可以通過Shell物件來進行。而建立好的AndroidShellHolder物件指標值又返回給了java層,最終java層便可以使用該指標值通過JNI方法呼叫拿到Embedder層的AndroidShellHolder物件,進而通過Shell物件向engine層傳送一系列操作指令。
這個階段關鍵c++類的UML類圖
flutter中dart層程式碼執行
以上流程已經為dart層程式碼執行建立好了執行時環境,那麼接下來就應該載入dart層相關的程式碼執行了,這樣我們就可以看到dart編寫的widget顯示在MainActivity介面上了。
我們回到上面分析的FlutterActivityDelegate的onCreate()方法中,當FlutterView和FlutterNativeView建立成功後,會通過activity.setContentView(flutterView);
將FlutterView作為activity的內容檢視,而flutter層的UI就是被渲染到FlutterView上的,所以當前MainActivity展示出來的就是我們的flutter UI介面。
當然,程式碼走到此時,dart層程式碼還沒有執行,所以介面上還是顯示空白,我們看onCreate()程式碼塊的最後部分,找到appBundle然後通過runBundle方法開始執行,runBundle方法如下
- /flutter/shell/platform/android/io/flutter/app/FlutterActivityDelegate.java
private void runBundle(String appBundlePath) {
if (!flutterView.getFlutterNativeView().isApplicationRunning()) {
FlutterRunArguments args = new FlutterRunArguments();
ArrayList<String> bundlePaths = new ArrayList<>();
ResourceUpdater resourceUpdater = FlutterMain.getResourceUpdater();
if (resourceUpdater != null) {
File patchFile = resourceUpdater.getInstalledPatch();
JSONObject manifest = resourceUpdater.readManifest(patchFile);
if (resourceUpdater.validateManifest(manifest)) {
bundlePaths.add(patchFile.getPath());
}
}
bundlePaths.add(appBundlePath);
args.bundlePaths = bundlePaths.toArray(new String[0]);
args.entrypoint = "main";
flutterView.runFromBundle(args);
}
}
複製程式碼
第一次啟動flutter頁面isApplicationRunning()為false,執行if語句後的程式碼塊,先檢查是否有更新的flutter相關資源(用於動態更新,2019年flutter團隊的一個目標之一,這裡是先預埋了程式碼,應該還沒有起作用),沒有更新的bundle包,則設定對應的執行引數,然後使用flutterView.runFromBundle()
方法開始執行。
- /flutter/shell/platform/android/io/flutter/view/FlutterView.java
public void runFromBundle(FlutterRunArguments args) {
assertAttached();
preRun();
mNativeView.runFromBundle(args);
postRun();
}
複製程式碼
呼叫FlutterNativeView的runFromBundle方法執行
- /flutter/shell/platform/android/io/flutter/view/FlutterNativeView.java
public void runFromBundle(FlutterRunArguments args) {
boolean hasBundlePaths = args.bundlePaths != null && args.bundlePaths.length != 0;
...
if (hasBundlePaths) {
runFromBundleInternal(args.bundlePaths, args.entrypoint, args.libraryPath);
} else {
...
}
}
private void runFromBundleInternal(String[] bundlePaths, String entrypoint,
String libraryPath) {
...
mFlutterJNI.runBundleAndSnapshotFromLibrary(
bundlePaths,
entrypoint,
libraryPath,
mContext.getResources().getAssets()
);
applicationIsRunning = true;
}
複製程式碼
最終是通過FlutterJNI的方法來呼叫JNI方法執行
- /flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
@UiThread
public void runBundleAndSnapshotFromLibrary(
@NonNull String[] prioritizedBundlePaths,
@Nullable String entrypointFunctionName,
@Nullable String pathToEntrypointFunction,
@NonNull AssetManager assetManager
) {
ensureAttachedToNative();
nativeRunBundleAndSnapshotFromLibrary(
nativePlatformViewId,
prioritizedBundlePaths,
entrypointFunctionName,
pathToEntrypointFunction,
assetManager
);
}
private native void nativeRunBundleAndSnapshotFromLibrary(
long nativePlatformViewId,
@NonNull String[] prioritizedBundlePaths,
@Nullable String entrypointFunctionName,
@Nullable String pathToEntrypointFunction,
@NonNull AssetManager manager
);
複製程式碼
到這裡,我們發現會呼叫JNI方法傳入nativePlatformViewId引數,這個就是我們前面提到的AndroidShellHolder物件的指標值,此時的prioritizedBundlePaths陣列中只有一個值類似"/data/data/包名/flutter/flutter_assets/"的路徑值,entrypointFunctionName為"main",pathToEntrypointFunction為null。接下來看下JNI對應c++方法的實現
- /flutter/shell/platform/android/platform_view_android_jni.cc
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") {
...
} else {
asset_manager->PushBack(
std::make_unique<blink::DirectoryAssetBundle>(fml::OpenDirectory(
bundlepath.c_str(), false, fml::FilePermission::kRead)));
...
}
}
auto isolate_configuration = CreateIsolateConfiguration(*asset_manager);
...
RunConfiguration config(std::move(isolate_configuration),
std::move(asset_manager));
{
auto entrypoint = fml::jni::JavaStringToString(env, jEntrypoint);
auto libraryUrl = fml::jni::JavaStringToString(env, jLibraryUrl);
if ((entrypoint.size() > 0) && (libraryUrl.size() > 0)) {
...
} else if (entrypoint.size() > 0) {
config.SetEntrypoint(std::move(entrypoint));
}
}
ANDROID_SHELL_HOLDER->Launch(std::move(config));
}
複製程式碼
該段程式碼首先將迴圈jbundlepaths中的資訊將根據bundlepath建立DirectoryAssetBundle物件放到交由asset_manager管理,然後建立執行配置物件config,最後通過ANDROID_SHELL_HOLDER->Launch(std::move(config));
根據執行配置資訊啟動。注意ANDROID_SHELL_HOLDER是一個巨集,具體實現為
- /flutter/shell/platform/android/platform_view_android_jni.cc
#define ANDROID_SHELL_HOLDER \
(reinterpret_cast<shell::AndroidShellHolder*>(shell_holder))
複製程式碼
即是將傳過來的java層持有的AndroidShellHolder指標值強轉為AndroidShellHolder物件指標,此時就可以通過物件指標呼叫其方法執行所需要的操作了。接下來看下Launch方法的實現
- /flutter/shell/platform/android/android_shell_holder.cc
void AndroidShellHolder::Launch(RunConfiguration config) {
...
shell_->GetTaskRunners().GetUITaskRunner()->PostTask(
fml::MakeCopyable([engine = shell_->GetEngine(), //
config = std::move(config) //
]() mutable {
...
if (!engine || engine->Run(std::move(config)) ==
shell::Engine::RunStatus::Failure) {
...
} else {
...
}
}));
}
複製程式碼
我們可以看到engine執行dart層程式碼是通過UITaskRunner在UI Thread中執行的,這就是前面說的建立UI Thread的主要作用,下面看下engine的Run方法
- /flutter/shell/common/engine.cc
Engine::RunStatus Engine::Run(RunConfiguration configuration) {
...
auto isolate_launch_status =
PrepareAndLaunchIsolate(std::move(configuration));
...
return isolate_running ? Engine::RunStatus::Success
: Engine::RunStatus::Failure;
}
shell::Engine::RunStatus Engine::PrepareAndLaunchIsolate(
RunConfiguration configuration) {
...
auto isolate_configuration = configuration.TakeIsolateConfiguration();
std::shared_ptr<blink::DartIsolate> isolate =
runtime_controller_->GetRootIsolate().lock();
...
if (configuration.GetEntrypointLibrary().empty()) {
if (!isolate->Run(configuration.GetEntrypoint())) {
...
}
} else {
...
}
return RunStatus::Success;
}
複製程式碼
最終會通過DartIsolate的Run方法來執行
- /flutter/runtime/dart_isolate.cc
bool DartIsolate::Run(const std::string& entrypoint_name) {
...
Dart_Handle entrypoint =
Dart_GetField(Dart_RootLibrary(), tonic::ToDart(entrypoint_name.c_str()));
...
Dart_Handle isolate_lib = Dart_LookupLibrary(tonic::ToDart("dart:isolate"));
if (tonic::LogIfError(isolate_lib)) {
return false;
}
Dart_Handle isolate_args[] = {
entrypoint,
Dart_Null(),
};
if (tonic::LogIfError(Dart_Invoke(
isolate_lib, tonic::ToDart("_startMainIsolate"),
sizeof(isolate_args) / sizeof(isolate_args[0]), isolate_args))) {
return false;
}
...
return true;
}
複製程式碼
通過entrypoint_name建立執行入口Dart控制程式碼entrypoint,通過Dart_LookupLibrary方法查詢"dart:isolate"庫的控制程式碼isolate_lib,這裡需要注意isolate_args[]控制程式碼陣列,第一個值為entrypoint,第二個值為Dart_Null(),然後通過Dart_Invoke方法呼叫執行
- /third_party/dart/runtime/vm/dart_api_impl.cc
DART_EXPORT Dart_Handle Dart_Invoke(Dart_Handle target,
Dart_Handle name,
int number_of_arguments,
Dart_Handle* arguments) {
...
String& function_name =
String::Handle(Z, Api::UnwrapStringHandle(Z, name).raw());
...
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(target));
...
if (obj.IsType()) {
...
} else if (obj.IsNull() || obj.IsInstance()) {
...
} else if (obj.IsLibrary()) {
// Check whether class finalization is needed.
const Library& lib = Library::Cast(obj);
...
if (Library::IsPrivate(function_name)) {
function_name = lib.PrivateName(function_name);
}
...
return Api::NewHandle(
T, lib.Invoke(function_name, args, arg_names, respect_reflectable));
} else {
...
}
}
複製程式碼
通過上面isolate_lib的建立方法Dart_LookupLibrary的實現可知obj為一個Library物件,最後通過lib.Invoke()
方法來執行dart方法,dart中對應的具體方法實現為
- /third_party/dart/runtime/lib/isolate_patch.dart
@pragma("vm:entry-point")
void _startMainIsolate(Function entryPoint, List<String> args) {
_startIsolate(
null, // no parent port
entryPoint,
args,
null, // no message
true, // isSpawnUri
null, // no control port
null); // no capabilities
}
複製程式碼
第一個引數entryPoint即為前文通過"main"查詢的main()入口函式,也即是我們編寫的dart中main.dart檔案中的main()函式,args為null最終通過呼叫以下_startIsolate方法執行。
- /third_party/dart/runtime/lib/isolate_patch.dart
@pragma("vm:entry-point")
void _startIsolate(
SendPort parentPort,
Function entryPoint,
List<String> args,
var message,
bool isSpawnUri,
RawReceivePort controlPort,
List capabilities) {
if (controlPort != null) {
controlPort.handler = (_) {}; // Nobody home on the control port.
}
...
RawReceivePort port = new RawReceivePort();
port.handler = (_) {
port.close();
if (isSpawnUri) {
if (entryPoint is _BinaryFunction) {
(entryPoint as dynamic)(args, message);
} else if (entryPoint is _UnaryFunction) {
(entryPoint as dynamic)(args);
} else {
entryPoint();
}
} else {
entryPoint(message);
}
};
// Make sure the message handler is triggered.
port.sendPort.send(null);
}
複製程式碼
我們在mait.dart中定義的main()函式並沒有任何引數,最後直接通過entryPoint()
將main()函式調起。到此,我們的dart層程式碼就執行起來了。後續就是通過main()函式中的runApp()方法呼叫開始執行各種Widget相關繫結、Element的建立、RenderObject的建立,然後合成幀資料供下一次gsync訊號接收時渲染資料到SurfaceView上。
iOS平臺程式碼分析
iOS平臺對應的Embedder層程式碼在engine原始碼的/flutter/shell/platform/darwin/
目錄下
我們同樣根據flutter create my_app
命令建立的Flutter專案demo來分析iOS平臺上flutter的啟動流程。
AppDelegate繼承自FlutterAppDelegate,我們看下FlutterAppDelegate的生命週期執行情況
- /flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm
- (instancetype)init {
if (self = [super init]) {
_lifeCycleDelegate = [[FlutterPluginAppLifeCycleDelegate alloc] init];
}
return self;
}
- (BOOL)application:(UIApplication*)application
willFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
return [_lifeCycleDelegate application:application willFinishLaunchingWithOptions:launchOptions];
}
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
return [_lifeCycleDelegate application:application didFinishLaunchingWithOptions:launchOptions];
}
複製程式碼
和安卓中類似,FluttAppDelegate生命週期方法中的處理由代理類物件_lifeCycleDelegate做具體處理,init方法中會對該物件進行初始化,我們先看下初始化和對應的生命週期代理方法都做了什麼
- /flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm
static const char* kCallbackCacheSubDir = "Library/Caches/";
- (instancetype)init {
if (self = [super init]) {
std::string cachePath = fml::paths::JoinPaths({getenv("HOME"), kCallbackCacheSubDir});
[FlutterCallbackCache setCachePath:[NSString stringWithUTF8String:cachePath.c_str()]];
_pluginDelegates = [[NSPointerArray weakObjectsPointerArray] retain];
}
return self;
}
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
for (id<FlutterPlugin> plugin in [_pluginDelegates allObjects]) {
if (!plugin) {
continue;
}
...
}
return YES;
}
- (BOOL)application:(UIApplication*)application
willFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
blink::DartCallbackCache::LoadCacheFromDisk();
for (id<FlutterPlugin> plugin in [_pluginDelegates allObjects]) {
if (!plugin) {
continue;
}
...
}
return YES;
}
複製程式碼
初始化方法中會獲取一個快取目錄,並設定到FlutterCallbackCache中。而兩個生命週期代理方法中會遍歷_pluginDelegates中的物件,但此時陣列中還沒有資訊。 接下來我們看下Main.storyboard會發現應用的rootViewController為FlutterViewController,那我們來看一下FlutterViewController的初始化和生命週期方法
- /flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm
- (instancetype)initWithProject:(FlutterDartProject*)projectOrNil
nibName:(NSString*)nibNameOrNil
bundle:(NSBundle*)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_viewOpaque = YES;
_weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterViewController>>(self);
_engine.reset([[FlutterEngine alloc] initWithName:@"io.flutter"
project:projectOrNil
allowHeadlessExecution:NO]);
_flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]);
[_engine.get() createShell:nil libraryURI:nil];
_engineNeedsLaunch = YES;
[self loadDefaultSplashScreenView];
[self performCommonViewControllerInitialization];
}
return self;
}
複製程式碼
初始化方法中會建立FlutterEngine物件、FlutterView物件,並根據FlutterEngine物件建立Shell物件,我們先看下FlutterEngine物件的建立
- /flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
- (instancetype)initWithName:(NSString*)labelPrefix
project:(FlutterDartProject*)projectOrNil
allowHeadlessExecution:(BOOL)allowHeadlessExecution {
...
_allowHeadlessExecution = allowHeadlessExecution;
_labelPrefix = [labelPrefix copy];
_weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterEngine>>(self);
if (projectOrNil == nil)
_dartProject.reset([[FlutterDartProject alloc] init]);
else
_dartProject.reset([projectOrNil retain]);
_pluginPublications = [NSMutableDictionary new];
_platformViewsController.reset(new shell::FlutterPlatformViewsController());
[self setupChannels];
return self;
}
複製程式碼
此時傳入的projectOrNil為nil,則會建立FlutterDartProject物件並儲存到_dartProject中,FlutterDartProject初始化
- /flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm
static blink::Settings DefaultSettingsForProcess(NSBundle* bundle = nil) {
auto command_line = shell::CommandLineFromNSProcessInfo();
NSBundle* mainBundle = [NSBundle mainBundle];
NSBundle* engineBundle = [NSBundle bundleForClass:[FlutterViewController class]];
...
auto settings = shell::SettingsFromCommandLine(command_line);
settings.task_observer_add = [](intptr_t key, fml::closure callback) {
fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback));
};
settings.task_observer_remove = [](intptr_t key) {
fml::MessageLoop::GetCurrent().RemoveTaskObserver(key);
};
...
return settings;
}
- (instancetype)init {
return [self initWithPrecompiledDartBundle:nil];
}
- (instancetype)initWithPrecompiledDartBundle:(NSBundle*)bundle {
self = [super init];
if (self) {
_precompiledDartBundle.reset([bundle retain]);
_settings = DefaultSettingsForProcess(bundle);
}
return self;
}
複製程式碼
初始化方法中會呼叫c++程式碼方法DefaultSettingsForProcess來完成_settings物件的初始化,主要是各種flutter資源路徑設定(國際化庫、framework庫等)和一些其他後續需要的資訊配置。
然後回到FlutterEngine初始化方法中,繼續完成各種platform channel的建立。
接著看下FlutterView物件的建立
- /flutter/shell/platform/darwin/ios/framework/Source/FlutterView.mm
- (instancetype)initWithDelegate:(id<FlutterViewEngineDelegate>)delegate opaque:(BOOL)opaque {
FML_DCHECK(delegate) << "Delegate must not be nil.";
self = [super initWithFrame:CGRectNull];
if (self) {
_delegate = delegate;
self.layer.opaque = opaque;
}
return self;
}
複製程式碼
做了很少的事情,主要是傳入並持有實現了FlutterViewEngineDelegate協議的FlutterEngine物件。最後看下Shell的建立
- /flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
- (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI {
...
static size_t shellCount = 1;
auto settings = [_dartProject.get() settings];
if (libraryURI) {
...
} else if (entrypoint) {
...
} else {
settings.advisory_script_entrypoint = std::string("main");
settings.advisory_script_uri = std::string("main.dart");
}
const auto threadLabel = [NSString stringWithFormat:@"%@.%zu", _labelPrefix, shellCount++];
fml::MessageLoop::EnsureInitializedForCurrentThread();
_threadHost = {
threadLabel.UTF8String, // label
shell::ThreadHost::Type::UI | shell::ThreadHost::Type::GPU | shell::ThreadHost::Type::IO};
shell::Shell::CreateCallback<shell::PlatformView> on_create_platform_view =
[](shell::Shell& shell) {
return std::make_unique<shell::PlatformViewIOS>(shell, shell.GetTaskRunners());
};
shell::Shell::CreateCallback<shell::Rasterizer> on_create_rasterizer = [](shell::Shell& shell) {
return std::make_unique<shell::Rasterizer>(shell.GetTaskRunners());
};
if (shell::IsIosEmbeddedViewsPreviewEnabled()) {
...
} else {
blink::TaskRunners task_runners(threadLabel.UTF8String, // label
fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform
_threadHost.gpu_thread->GetTaskRunner(), // gpu
_threadHost.ui_thread->GetTaskRunner(), // ui
_threadHost.io_thread->GetTaskRunner() // io
);
// Create the shell. This is a blocking operation.
_shell = shell::Shell::Create(std::move(task_runners), // task runners
std::move(settings), // settings
on_create_platform_view, // platform view creation
on_create_rasterizer // rasterzier creation
);
}
...
return _shell != nullptr;
}
複製程式碼
該方法的實現和安卓中的AndroidShellHolder構造方法實現類似,主要新建立了三個執行緒gpu_thread
、ui_thread
和io_thread
,加上平臺的UI執行緒作為platform_thread
一共四個關鍵執行緒。執行緒的作用可以參考安卓中的說明。然後會通過Shell::Create()
方法建立engine層的Shell物件,後邊的engine初始化、DartVM初始化和其他一系列物件建立都和安卓上面分析的一樣了,這裡不再贅述。
回過頭來看一下FlutterViewController中初始化方法,上面一系列操作執行後,會進行各種必要的通知註冊,以便接收到通知後做出響應。
至此FlutterViewController的初始化就完成了,我們可以看出整個的初始化就是對flutter engine的初始化,那麼iOS平臺dart層程式碼是在哪一步執行的呢,我們下面看一下FlutterViewController的生命週期方法viewWillAppear方法
- /flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm
- (void)viewWillAppear:(BOOL)animated {
TRACE_EVENT0("flutter", "viewWillAppear");
if (_engineNeedsLaunch) {
[_engine.get() launchEngine:nil libraryURI:nil];
_engineNeedsLaunch = NO;
}
[_engine.get() setViewController:self];
if (_viewportMetrics.physical_width)
[self surfaceUpdated:YES];
[[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.inactive"];
[super viewWillAppear:animated];
}
複製程式碼
這裡_engineNeedsLaunch在FlutterViewController初始化的時候被設定為YES,則會通過_engine開始啟動引擎
- /flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
- (void)launchEngine:(NSString*)entrypoint libraryURI:(NSString*)libraryOrNil {
// Launch the Dart application with the inferred run configuration.
self.shell.GetTaskRunners().GetUITaskRunner()->PostTask(fml::MakeCopyable(
[engine = _shell->GetEngine(),
config = [_dartProject.get() runConfigurationForEntrypoint:entrypoint
libraryOrNil:libraryOrNil] //
]() mutable {
if (engine) {
auto result = engine->Run(std::move(config));
if (result == shell::Engine::RunStatus::Failure) {
FML_LOG(ERROR) << "Could not launch engine with configuration.";
}
}
}));
}
複製程式碼
這裡engine執行dart層程式碼是通過UITaskRunner在UI Thread中執行的,該方法和安卓Embedder層AndroidShellHolder的Launch方法功能相同。不過我們發現該方法兩個引數都為nil,而安卓中的entrypoint為"main",那麼iOS中最終是如何執行Dart應用程式執行main()方法的呢,我們看下RunConfiguration類的宣告中一些變數的宣告
- /flutter/shell/common/run_configuration.cc
class RunConfiguration {
public:
...
RunConfiguration(RunConfiguration&&);
~RunConfiguration();
...
const std::string& GetEntrypoint() const;
const std::string& GetEntrypointLibrary() const;
...
private:
...
std::string entrypoint_ = "main";
std::string entrypoint_library_ = "";
FML_DISALLOW_COPY_AND_ASSIGN(RunConfiguration);
}
複製程式碼
結果我們發現RunConfiguration物件建立時預設entrypoint即為"main",所以不用主動設定entrypoint,最終Dart程式碼執行時就會將main()方法作為入口函式執行。後續的一些engine層的操作和安卓一樣了,這裡也不再贅述。
這個階段關鍵objective-c和c++類的UML類圖
總結
通過以上安卓和iOS平臺的原始碼分析,我們已經對flutter應用的啟動有了一個大致的瞭解,而Dart中編寫的Widget最終是如何繪製到平臺View上的呢,大家可以閱讀之前本號分享的一篇文章《Flutter檢視繪製》,相信大家會得到想要的答案。
說明:
文章轉載自對應的“Flutter程式設計指南”微信公眾號,更多Flutter相關技術文章開啟微信掃描二維碼關注微信公眾號獲取。