flutter底層原理和embedder的隱憂

共田君發表於2019-06-15

從Flutter technical-overview基本架構來說framework是使用最頻繁的,但是對於engine和embedder確實flutter的底層,支援整個flutter的執行

  • 本文圖片參考於閒魚文章
  • 架構圖參考flutter.dev

flutter底層原理和embedder的隱憂

flutter 有三個學習層次,framework,engine,embedder 上層的framework負責ui相關的事情,動畫,widget,繪圖,手勢,基礎庫 engine層次c++實現,支援flutter相關的渲染,執行緒管理,平臺事件。

engine裡面有個記憶體洩漏,flutter官方一直沒有解決,可以出門左轉找到解決方案《手把手教你解決flutter記憶體洩漏》。一句話就是,flutter在處理flutter method channel和register與engine之間持有關係比較混亂,存在一個比較大的迴圈引用。

flutter底層原理和embedder的隱憂

embedder 為engine提供四個task runner,將引擎移植到平臺的中間層程式碼 渲染設定,原生外掛,打包,執行緒管理,事件迴圈互動操作

在shell.cc裡面可以看到,四個runner包括 ui runner gpu runner、io runner、platform runner

  • ui runner負責繫結渲染相關的操作,如通知engine有幀渲染,等待下一個vsync,對建立的widget進行layout並生成layer tree,更新layer tree資訊,通知處理native plugin資訊,如timer,microtask,非同步io操作

  • gpu runner是用於執行gpu指令,負責將layer tree提供的資訊轉換為平臺可執行的gpu指令,負責繪製gpu資源的管理,包括framebuffer,surface、texture、buffers

  • ios runner是處理圖片資料,為gpu渲染做準備,比如讀取磁碟壓縮圖片的格式,將解壓成gpu能處理的格式,並傳給gpu,因其比較消耗效能所以單獨開一個執行緒。

  • platform runner,所有介面呼叫都使用該介面,長時間卡頓將會被watchdog強殺。

runner的實現策略,ios、android平臺啟動時為ui,gpu,io runner各自建立一個執行緒,但是共享platform runner和執行緒 Fushia,為ui,gpu,io,platform各自建立一個執行緒。

flutter底層原理和embedder的隱憂

isolate 是dart vm自己管理,engine無法訪問,它是對actor併發模式的實現,執行中程式由一個或多個actor組成,這些actor就是isolate。

原意是隔離的意思,表示沒有共享記憶體和併發,邏輯上隔離,按順序執行,不用擔心死鎖的問題。 isolate直接的通訊方式只能通過port,訊息傳遞非同步,與普通執行緒最大的區別就是沒有共享記憶體。 實現的步驟有,初始化isolate資料結構,初始化堆記憶體,進入對應所在的執行緒執行isolate,配置port,配置訊息處理機制,配置debugger,將isolate註冊到全域性監控器。

與runner的關係,isolate是dart vm自己管理,engine無法訪問。 如對於ui runner來說,isolate通過dart的c++呼叫能力,將ui渲染任務提交到ui runner,這樣可以跟engine相關模組進行互動,ui相關的任務被提交到ui runner也給isolate一些事件通知,所以isolate同時處理app和native plugin的任務 對於gpu rnnner來說,isloate將dart的c++呼叫能力把gpu指令提交給gpu runner,把layer tree資訊轉換為gpu指令,這些都是通過isolate來執行的

分享一個坑,emmberdder裡面有一個fml weakptr會造成四個task runner在reset的時候釋放,但是不會把指標置空,會在一定機率下關閉flutterviewcontroller,造成崩潰,因為訪問了野指標,fml的實現使用了uniqueptr智慧指標,如果控制不好,這個應該後期會給flutter帶來比較大的崩潰率。

測試程式碼

-(void)handleAutoRelase{
 
     FlutterBasicMessageChannel* channel;
    FlutterEngine * engine;
    @autoreleasepool {
        FlutterViewController* flutterViewController =
        [[FlutterViewController alloc] init];
        channel = flutterViewController.engine.systemChannel;
        engine = flutterViewController.engine;
        NSLog(@"engine111:%@",engine);
    }
    NSLog(@"engine222:%@",engine);
    [channel sendMessage:@"Hello!"];
    [channel setMessageHandler:^(id  _Nullable message, FlutterReply  _Nonnull callback) { }]; 
}
複製程式碼

以上程式碼是google工程師提供的測試程式碼,autoreleasepool中包括了flutter和engine的建立,然後自動釋放,然後在釋放之後重新呼叫sendmessage的方法,此時會有一個訪問野指標的崩潰。 對於engine的改寫就需要放置在釋放的時候放置對內部方法的訪問

flutter底層原理和embedder的隱憂

這樣可以防止釋放時候崩潰,但是對於根本的原因是fml內部實現的問題,如上所說,釋放完成而指標變成了懸空指標。

engine的第二個隱患,在shell.cc訪問weakptr一定會得到一個不為空的指標,即使是在engine或platformview釋放的時候,以下是它的實現程式碼

flutter底層原理和embedder的隱憂

flutter底層原理和embedder的隱憂

粗略算了一下,四個taskrunner的getweakptr方法的實現都有隱患,歸根到底還是fml的實現問題,懸空指標沒有解決,這些都會造成野指標訪問記憶體的崩潰。

flutter底層原理和embedder的隱憂

相關文章