探索Flutter跨平臺執行機制
終於,又滿足了自己的一個好奇心!
利用中秋假期時間探索了Flutter是如何在Android、iOS上執行起來的,因此,這篇文章主要分享Flutter在Android、iOS上的執行機制。至於Flutter是什麼?Flutter Framework是如何架構的?Dart是什麼?等等一些好奇點,待後續有時間、有機會再探索。
首先,建立一個新的Flutter專案

可以看到android、ios、lib三個主要目錄,個人更熟悉Android一些,那麼就從android開始探索吧。
在settings.gradle、build.gradle、app/build.gradle等編譯配置檔案中發現以下重要資訊

可以看到,編譯過程中引入了Flutter SDK中的flutter.gradle編譯配置檔案,具體檢視flutter.gradle可以發現以下重要資訊

可以看到在編譯過程中,會在bin、cache等目錄下尋找flutter.jar,如果沒有會執行flutter precahe命令,在Flutter SDK中快取flutter.jar檔案,然後將其打包到apk中。繼續向下看,還發現如下程式碼

這段程式碼初步看起來是在gradle構建過程中插入了flutterBuild和copyFlutterAssets的task,flutterBuild通過執行flutter指令將flutter相關程式碼進行編譯,然後copyFlutterAssets將之前編譯生成的flutter assets複製到Android Assets路徑下,接下來通過實際編譯驗證。在編譯過程中看到如下關鍵輸出

可以看到flutterBuild的確執行了flutter build aot、flutter build bundle命令,其中flutter build aot --target帶的引數正是lib/main.dart,編譯生成產物輸出到build/app/intermediates/flutter/release資料夾下。flutter build bundle 同樣帶了--target lib/main.dart,在build/app/intermediates/flutter/release下生成了flutter_assets資料夾。具體詳情如下

至此,編譯流程就已經探索完成了,接下來探索執行流程,在android/app下找到AndroidManifest檔案,發現以下內容

看到了FlutterApplication以及MainActivity,通過檢視程式碼MainActivity是繼承自FlutterActivity的。接下來就需要檢視FlutterApplication、FlutterActivity等原始碼了。根據常識這部分原始碼就是剛編譯過程中看到的flutter.jar的內容,在Flutter SDK中未找到原始碼,苦苦尋找,在github flutter倉庫中發現了engin工程,原始碼居然深藏於此。

首先,檢視FlutterApplication程式碼

可以主要通過FlutterMain進行了初始化工作了,簡單檢視FlutterMain的程式碼

可以看到initAot、initResource方法,也就是編譯過程中提到的flutterBuile生成的相關檔案。最後load了flutter.so native檔案。接下來檢視FlutterActivity原始碼

可以發現,核心程式碼基本都在Delegate類裡邊,繼續檢視

可以發現,給Activity設定了FlutterView作為ContentView,然後呼叫了FlutterView.runFromBundle。首先檢視FlutterView

可以發現FlutterView繼承自Android SurfaceView/">SurfaceView,並且在SurfaceHolder.Callback surfaceCreated中呼叫了nativeSurfaceCreated,並將surface傳入進去進行繪製顯示。同時,在FlutterView的構造方法中也看到例項化了mFlutterNavigationChannel、mFlutterSystemChannel等MethodChannel、BasicMessageChannel等物件。接著檢視FlutterView.runFromBundle,通過程式碼呼叫最終呼叫到了FlutterNativeView的runFromBundleInternal

可以看到最終呼叫了nativeRunBundleAndSnapshotFromLibrary,並且傳入了bundlePath,entrypoint,Android AssertManager。之前編譯生成的flutter assets就存放在這些位置。在 ofollow,noindex"> http:// platform_view_android_jni.cc 中找到nativeRunBundleAndSnapshotFromLibrary的實現

可以看到根據傳入的引數構造出了RunConfiguration物件,然後Launch FlutterApp。
至此,Flutter在Android平臺執行機制的探索就告一段落了。總結整體流程,編譯階段將lib下dart檔案編譯生成flutter目錄下的flutter_assets以及snapshot等檔案,然後打包進Android Apk。執行階段,生成FlutterView並設定給Acitivity ContentView,FlutterView將surface暴露給底層渲染引擎,同時也會生成通訊channel,最後將編譯生成的檔案通過nativeRunBundleAndSnapshotFromLibrary暴露給flutter底層引擎,以此啟動FlutterApp,底層引擎執行過程中通過FlutterNativeView與FlutterView進行互動。
至於底層引擎,後續有機會繼續深入探索,接下來探索iOS平臺執行機制。
用Xcode開啟之前建立工程的ios目錄,可以看到如下編譯hook

可以看到了引入了Flutter SDK中的xcode_backend.sh指令碼,具體檢視

可以看到將Flutter.framework複製到了工程目錄下的Flutter資料夾內,繼續檢視

可以看到同樣執行了flutter build aot、flutter build bundle並將編譯產物複製到了工程Flutter目錄下,結果如下

在iOS編譯生成的.app檔案中同樣也可以看到flutter_assets

至此,iOS編譯流程也探索完成了,接下來同樣探索執行流程

在Main.storyboard中可以發現,其使用了FlutterViewController,接下來在Flutter SDK中找到FlutterViewController

緊接著檢視FlutterViewController.mm原始碼

可以發現同樣建立了FlutterView以及setup了用來通訊的channel,首先來看FlutterView

可以發現,FlutterView的layerClass返回了CAEAGLLayer,詳情可以瞭解 CAEAGLLayer - Core Animation 。接著檢視FlutterViewController的init方法,最後呼叫到了performCommonViewControllerInitialization,具體檢視

可以發現依次呼叫了setupShell、setupChannel等,檢視setupShell方法

可以發現通過task_runners、dartProject setting等Launch了FlutterApp,具體檢視FlutterDartProject

可以發現initWithFlutterAssets進而呼叫DefaultSettingForProcess將編譯成生成的flutter_assets暴露給底層引擎。
至此,Flutter在iOS平臺執行機制的探索也告一段落了。總結整體流程,編譯階段通過呼叫Flutter SDK編譯指令碼,編譯生成flutter_assets資料夾,並複製到工程路徑下,然後打包.app檔案,執行階段主介面通過FlutterViewController執行,FlutterViewController loadView顯示FlutterView,FlutterView暴露surface給底層引擎,FlutterViewController init方法內呼叫了setupShell啟動Flutter App,setupChannel通訊相關,在setupShell通過FlutterDartProject將flutter_assets暴露給底層引擎。
綜合來看,Android、iOS平臺的執行機制基本一致,編譯階段生成flutter_assets,並打包進平臺安裝包。執行階段暴露渲染surface給底層引擎,同時通過平臺API將之前編譯生成的flutter_assets暴露給底層引擎。
對Flutter Framework架構、Flutter engine、Dart編譯等同樣好奇,但願後續有時間、機會能夠繼續探索!