Flutter混合開發——一種另類卻高效的的原生View嵌入方法

吉哈達發表於2020-07-26

前言

隨著使用Flutter開發的深入加之其生態還不完善,必然會涉及到使用原生View的情況。為此,Flutter也為我們提供了PlatformView方便我們嵌入原生View,以實現一些flutter暫時不支援的功能,但由此也引發了一些效能問題。

Flutter與原生View簡介

實現

這裡以Android來做一個簡介(如果沒用過原生View的話,可以百度教程),當我們需要使用一個Android的view時,我們在android端分別實現

你的類 extends PlatformView
你的類 extends PlatformViewFactory
複製程式碼

之後再通過registerViewFactory註冊你的View,並設定一個ID。

我們在Flutter端,建立一個AndroidView,並通過上面的ID就可以獲取到我們建立的androidView,並使用。

原理

那麼Flutter使用原生View的背後是怎麼實現的呢? 這裡可以參考大佬們的文章:

Flutter完整開發實戰詳解(二十、 Android PlatformView 和鍵盤問題)

萬萬沒想到——flutter這樣外接紋理

從上面的文章我們可以知道,原生View生成texture資料同時自帶一個textureId,資料再寫入PixelBuffer後,flutter會通過這個ID到PixelBuffer中取到對應的資料並通過skia渲染。

借一張圖

Flutter混合開發——一種另類卻高效的的原生View嵌入方法

流程就是:

GPU->CPU->GPU
複製程式碼

這樣也就造成了資源的浪費,增加耗時。那麼有沒有其他的方法呢?

另類的方法

(還是以Android為例)我們知道,flutter啟動是從FlutterActivity的子類MainActivity(你也可以建立自己的)onCreate開始的:

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    switchLaunchThemeForNormalTheme();

    super.onCreate(savedInstanceState);

    lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);

    delegate = new FlutterActivityAndFragmentDelegate(this);
    delegate.onAttach(this);
    delegate.onActivityCreated(savedInstanceState);

    configureWindowForTransparency();
    setContentView(createFlutterView());
    configureStatusBarForFullscreenFlutterExperience();
  }
複製程式碼

通過:

    delegate = new FlutterActivityAndFragmentDelegate(this);
    delegate.onAttach(this);
    delegate.onActivityCreated(savedInstanceState);
複製程式碼

會執行一系列flutter的初始化操作,並拿到android sruface進而完成啟動,展開渲染繪製。 具體可以參考flutter啟動流程相應的文章。

新增一個View

不管 FlutterActivity 啟動了啥,它終歸還是個Activity

class FlutterActivity extends Activity...
複製程式碼

該有的原生生命週期他都有,換言之我們可以在這裡進行原生開發(至少從程式碼上看是可以的),那麼我們嘗試新增個view試試, 在MainActivity的

public void configureFlutterEngine(FlutterEngine flutterEngine){
...
}
複製程式碼

我們延遲新增一個View

        new Handler()
                .postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        this.addContentView(你的View);
                    }
                },2000);
複製程式碼

之所以延遲新增,是因為不延遲的話,你將看不到它。(這裡我粗略看了一下啟動程式碼,可能是因為過早繪製被flutter遮擋了)。

專案啟動後一小會:

Flutter混合開發——一種另類卻高效的的原生View嵌入方法

成功了!哈哈哈!(本人菜雞,所以有些興奮請勿見怪,嘿嘿)

效能對比

具本菜雞的分cai析ce,這種兩端繪製,應該是少走了GPU->CPU->GPU的路線,怎麼應該強一些吧,但是具體如何呢? 還是需要比較一下。

測試環境:

效能模式下測試
複製程式碼

測試用例:

使用listview,載入30個svg動畫,並極快速的滾動
複製程式碼

Flutter混合開發——一種另類卻高效的的原生View嵌入方法

每一個小惡魔都是一個svg。

經過一些系列操作後,得出如下結果

AndroidView 測試結果

Flutter提供的AndroidView會有非常非常明顯的卡頓,丟幀和圖片延遲顯示:

快速滑動的時候尤其明顯,performance圖例:

Flutter混合開發——一種另類卻高效的的原生View嵌入方法

Flutter混合開發——一種另類卻高效的的原生View嵌入方法

原生端新增式 測試結果

具體資料如下:

圖1:

Flutter混合開發——一種另類卻高效的的原生View嵌入方法

圖2:

Flutter混合開發——一種另類卻高效的的原生View嵌入方法

在頁面剛載入時,快速滑動,耗時較多(見圖1),之後快速滑動FPS就可以穩定在3ms左右(見圖2),實際使用感受非常之流暢。

Flutter外掛 測試結果

另外我還做了一下flutter使用外掛的效果(flutter_svg),如下圖:

圖1:

Flutter混合開發——一種另類卻高效的的原生View嵌入方法

圖2:

Flutter混合開發——一種另類卻高效的的原生View嵌入方法

和 原生端新增 方式差不多,在頁面初始化時,快速滾動耗時較多,隨後繪製耗時趨於平穩並且穩定在3ms左右,不過有個問題就是,每次快速滑動,都會有一個FPS波峰,如圖2,但整體使用感受是非常流暢的。

後語

至此整個測試流程結束,通過測試結果對於效能的差異也可見一斑(實際上我還做了一些其他的測試,使用感受均是‘原生端新增’要比 ‘原生View’流暢),不過對於這裡的研究我還在繼續,以上的這些結論還是我憑藉自己的一點兒經驗知識和測試結果所推測出來的,後續有什麼發現會再分享給大家。

如果有錯的地方或者什麼想法歡迎提出來,大家一起探討,謝謝。

相關文章