Flutter 官方在 GitHub 上宣告是暫時不支援熱更新的,但是在 Flutter 的原始碼裡,是有一部分預埋的熱更新相關的程式碼,並且通過一些我們自己的手段,在Android端是能夠實現動態更新的功能的。
Flutter 產物的探究不論是建立完全的 Flutter專案,還是 Native以 Moudle得方式整合 Flutter,亦或是 Native以 aar方式整合 Flutter,最終 Flutter在 Andorid端的 App 都是以 Native專案+ Flutter 的UI產物存在的。所以在這裡拆開一個 Flutter在 release模式下編譯後生成 aar包來做分析:
我們關注重點在 assets,jni,libs 這 3 個目錄中,其他的檔案都是 Nactive層殼工程的產物。
jni :該目錄下存在檔案 libflutter.so,該檔案為 Flutter Engine (引擎) 層的 C++實現,提供skia(繪製引擎),Dart,Text(紋理繪製)等支援;
libs:該目錄下存在檔案為 flutter.jar,該檔案為 Flutter embedding (嵌入) 層的 Java實現,該層提供給 Flutter 許多Native層平臺系統功能的支援,比如建立執行緒。
assets:該目錄下分為兩部分:
flutter_assets 目錄:該目錄下存放Flutter 我們應用層的資源,包括images,font等isolate_snapshot_data,isolate_snapshot_instr,vm_snapshot_data,vm_snapshot_instr 檔案:這 4 個檔案分別對應 isolate、VM 的資料段和指令段檔案,這就是我們自己的 Flutter 程式碼的產物了。Flutter 程式碼的熱更新程式碼探究:
在我們的 Native 專案中,會在 FlutterMainActivity 中,通過呼叫 Flutter 這個類來建立 View:
檢視 Flutter 類程式碼,發現 Flutter 類主要做了幾件事:
在 startInitialization 中,主要執行了三個初始化方法 initConfig(applicationContext),initAot(applicationContext),initResources(applicationContext),最後記錄了執行時間。
在 initConfig 中:
在 initResources 中:
在 ResourceExtractor 類中,通過名字就能知道這個類是做資源提取的。把 add 的 Flutter 相關檔案從 assets 目錄中取出來,該類中 ExtractTask 的 doInBackground 方法中:
File dataDir = new File(PathUtils.getDataDirectory(ResourceExtractor.this.mContext))
如圖,可以看到該目錄是的訪問許可權是可讀可寫,所以理論上,我們只要把自己的 Flutter 產物下載後,從記憶體 copy 到這裡,便能夠實現程式碼的動態更新。
完整的程式碼實現:
Flutter 資源的熱更新
我們的App安裝到手機上後,是很難再修改 Assets 目錄下的資源,所以關於資源的替換,目前的方案是使用 Flutter 的 API :Image.file() 來從儲存卡中讀取圖片。
通常我們的 Flutter 專案中應當存有關於 App 的圖片,儘量保證在熱更新的時候使用已經存在的圖片。
其次,我們可以使用 Image.network() 來載入網路資源的圖片,如果還不能滿足需求,兜底的方案就是使用 Image.file(),將資源圖片放到Zip目錄下一起下發,並在Flutter程式碼中使用 Image.file() 來載入。
通過 Native 層方法拿到圖片資料夾的記憶體地址 dataDir判斷圖片是否存在,存在則載入,不存在則載入已經存在的圖片佔位new File(dataDir + 'hotupdate_test.png').existsSync()? Image.file(new File(dataDir + 'hotupdate_test.png')): Image.asset("images/net_error.png"),
總結在 Flutter 程式碼產物替換中,因為替換的 4 個檔案皆為直接載入到記憶體中的引擎程式碼,所以這部分優化空間有限。但在資源的熱更新中,資源是從Assets取得,所以這裡應該有更優的方案。
Flutter 的熱更新意味著可以在在App的一個入口裡,像 H5 一樣無窮的嵌入頁面,但又有和原生媲美的流暢體驗。
未來 Flutter 熱更新技術如果成熟,應用開發可能只需要 Android端和 IOS端實現本地業務功能模組的封裝,業務和UI的程式碼都放在 Flutter 中,便能夠真正的實現移動兩端一份業務程式碼,並且賦予產品在不影響使用者體驗的情況下,擁有動態部署APP內容的能力。