<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [TOC] # 前言 Flutter 作為目前最火爆的移動端跨平臺框架,能夠幫助開發者通過一套代碼庫高效地構建多平臺的精美應用,并支持移動、Web、桌面和嵌入式平臺。對于 Android 來說,Flutter 能夠創作媲美原生的高性能應用,但是,在較為復雜的 App 中,使用 Flutter 開發也很難避免產生各種各樣的性能問題。在這篇文章中,我將和你一起全方位地深入探索 Flutter 性能優化的疆域。 # 一、檢測手段 ### 準備 以 profile 模式啟動應用,如果是混合 Flutter 應用,在 flutter/packages/flutter\_tools/gradle/flutter.gradle 的 buildModeFor 方法中將 debug 模式改為 profile即可。 ### 為什么要在分析模式下來調試應用性能? 分析模式在發布模式的基礎之上,為分析工具提供了少量必要的應用追蹤信息。 ### 那,為什么要在發布模式的基礎上來調試應用性能? 與調試代碼可以在調試模式下檢測 Bug 不同,性能問題需要在發布模式下使用真機進行檢測。這是因為,相比發布模式而言,**調試模式增加了很多額外的檢查(比如斷言),這些檢查可能會耗費很多資源,而更重要的是,調試模式使用 JIT 模式運行應用,代碼執行效率較低。這就使得調試模式運行的應用,無法真實反映出它的性能問題**。 而另一方面,**模擬器使用的指令集為 x86,而真機使用的指令集是 ARM**。這兩種方式的二進制代碼執行行為完全不同,因此,模擬器與真機的性能差異較大,例如,針對一些 x86 指令集擅長的操作,模擬器會比真機快,而另一些操作則會比真機慢。這也同時意味著,你無法使用模擬器來評估真機才能出現的性能問題。 ## 1、Flutter Inspector Flutter Inspector有很多功能,但你應該把注意力花在更有用的功能學習上,例如:**“Select Widget Mode” 和 “Repaint Rainbow”**。 ### Select Widget Mode 點擊 “Select Widget Mode” 圖標,可以在手機上查看當前頁面的布局框架與容器類型。 ![](https://img.kancloud.cn/f4/ec/f4ec0df4bb86885b7de8118a626343ae_402x140.png) #### 作用 **快速查看陌生頁面的布局實現方式**。 ### Repaint Rainbow 點擊 “Repaint Rainbow” 圖標,它會?**為所有 RenderBox 繪制一層外框,并在它們重繪時會改變顏色**。 ![](https://img.kancloud.cn/f5/1e/f51efa77c48f5bed93c997a14b55a730_234x132.png) #### 作用 **幫你找到 App 中頻繁重繪導致性能消耗過大的部分**。 例如:一個小動畫可能會導致整個頁面重繪,這個時候使用 RepaintBoundary Widget 包裹它,可以將重繪范圍縮小至本身所占用的區域,這樣就可以減少繪制消耗。 #### 使用場景 例如?**頁面的進度條動畫刷新時會導致整個布局頻繁重繪**。 #### 缺點 **使用 RepaintBoundary Widget 會創建額外的繪制畫布,這將會增加一定的內存消耗**。 ## 2、性能圖層 性能圖層會在當前應用的最上層,以 Flutter 引擎自繪的方式展示 Raster 與 UI 線程的執行圖表,而其中每一張圖表都代表當前線程最近 300 幀的表現,如果 UI 產生了卡頓(跳幀),這些圖表可以幫助你分析并找到原因。 **藍色垂直的線條表示已執行的正常幀,綠色的線條代表的是當前幀,如果其中有一幀處理時間過長,就會導致界面卡頓,圖表中就會展示出一個紅色豎條**。 **如果紅色豎條出現在 GPU 線程圖表,意味著渲染的圖形太復雜,導致無法快速渲染;而如果是出現在了 UI 線程圖表,則表示 Dart 代碼消耗了大量資源,需要優化代碼的執行時間**。如下圖所示: ![](https://img.kancloud.cn/5c/fc/5cfc1b7030f3b39670b76d2af6329a3b_1080x397.png) ## 3、Raster 線程問題定位 它定位的是?**渲染引擎底層渲染的異常**。 解決方案是?**把需要靜態緩存的圖像加入到 RepaintBoundary。而 RepaintBoundary 可以確定 Widget 樹的重繪邊界,如果圖像足夠復雜,Flutter 引擎會自動將其緩存,避免重復刷新。當然,因為緩存資源有限,如果引擎認為圖像不夠復雜,也可能會忽略 RepaintBoundary**。 ## 4、UI 線程問題定位 ### 問題場景 在視圖構建時,在 build 方法中使用了一些復雜的運算,或是在主 Isolate 中進行了同步的 I/O 操作。 ### 使用 Performance 進行檢測 點擊 Android Studio 底部工具欄中的 “Open DevTools” 按鈕,然后在打開的 Dart DevTools 網頁中將頂部的 tab 切換到 Performance。 與性能圖層能夠自動記錄應用執行的情況不同,使用 Performance 來分析代碼執行軌跡,你需要手動點擊 “Record” 按鈕去主動觸發,在完成信息的抽樣采集后,點擊 “Stop” 按鈕結束錄制。這時,你就可以得到在這期間應用的執行情況了。 **使用 Performance 記錄應用的執行情況,即 CPU 幀圖,又被稱為火焰圖。火焰圖是基于記錄代碼執行結果所產生的圖片,用來展示 CPU 的調用棧,表示的是 CPU 的繁忙程度**。 其中: * **y 軸**:表示調用棧,其每一層都是一個函數。**調用棧越深,火焰就越高,底部就是正在執行的函數,上方都是它的父函數**。 * **x 軸**:表示單位時間,一**個函數在 x 軸占據的寬度越寬,就表示它被采樣到的次數越多,即執行時間越長**。 所以,我們要?**檢測 CPU 耗時問題,皆可以查看火焰圖底部的哪個函數占據的寬度最大。只要有 “平頂”,就表示該函數可能存在性能問題**。如下圖所示: ![](https://img.kancloud.cn/9f/37/9f37795aed22ae47c089201bd2160fa2_1080x629.png) 一般的耗時問題,我們通常可以?**使用 Isolate(或 compute)將這些耗時的操作挪到并發主 Isolate 之外去完成**。 > dart 的單線程執行異步任務是怎么實現的? 網絡調用的執行是由操作系統提供的另外的底層線程做的,而在 event queue 里只會放一個網絡調用的最終執行結果(成功或失敗)和響應執行結果的處理回調。 ## 5、使用 checkerboardOffscreenLayers 檢查多視圖疊加的視圖渲染 只要在 MaterialApp 的初始化方法中,將 checkerboardOffscreenLayers 開關設置為 true,分析工具就會自動幫你檢測多視圖疊加的情況。 這時,使用了 saveLayer 的 Widget 會自動顯示為棋盤格式,并隨著頁面刷新而閃爍。 而 saveLayer 一般會通過一些功能性 Widget,在涉及需要剪切或半透明蒙層的場景中間接地使用。 ## 6、使用 checkerboardRasterCacheImages 檢查緩存的圖像 它也是用來檢測在界面重繪時頻繁閃爍的圖像(即沒有靜態緩存)。解決方案是把需要靜態緩存的圖像加入到 RepaintBoundary。 # 二、關鍵優化指標 ## 1、頁面異常率 頁面異常率,即 頁**面渲染過程中出現異常的概率**。 它度量的是頁面維度下功能不可用的情況,其統計公式為: > 頁面異常率 = 異常發生次數 / 整體頁面 PV 數。 ### 統計異常發生次數 利用 Zone 與 FlutterError 這兩個方法,然后在異常攔截的方法中,去累計異常的發生次數。 ### 統計整體頁面 PV 數 繼承自 NavigatorObserver 的觀察者,并在其 didPush 方法中,去累加頁面的打開次數。 ## 2、頁面幀率 Flutter 在全局 Window 對象上提供了幀回調機制。我們可以在 Window 對象上注冊 onReportTimings 方法,將最近繪制幀耗費的時間(即 ?FrameTiming),以回調的形式告訴我們。 有了每一幀的繪制時間后,我們就可以計算 FPS 了。 為了讓 FPS 的計算更加平滑,我們需要保留最近 25 個 FrameTiming 用于求和計算。 由于幀的渲染是依靠 VSync 信號驅動的,如果幀繪制的時間沒有超過 16.67 ms,我們也需要把它當成 16.67 ms 來算,因為繪制完成的幀必須要等到下一次 VSync 信號來了之后才能渲染。而如果幀繪制時間超過了 16.67 ms,則會占用后續 VSync 的信號周期,從而打亂后續的繪制次序,產生卡頓現象。 那么,頁面幀率的統計公式就是: > FPS = 60 \* 實際渲染的幀數 / 本來應該在這個時間內渲染完成的幀數。 首先,定義一個容量為 25 的列表,用于存儲最近的幀繪制耗時 FrameTiming。 然后,在 FPS 的計算函數中,你再將列表中每幀繪制時間與 VSync 周期 frameInterval 進行比較,得出本來應該繪制的幀數。 最后,兩者相除就得到了 FPS 指標。 ## 3、頁面加載時長 > 頁面加載時長 = 頁面可見的時間 - 頁面創建的時間(包括網絡加載時長) ### 統計頁面可見的時間 **WidgetsBinding 提供了單次 Frame 回調的 addPostFrameCallback 方法,它會在當前 Frame 繪制完成之后進行回調,并且只會回調一次。一旦監聽到 Frame 繪制完成回調后,我們就可以確認頁面已經被渲染出來了**,因此我們可以借助這個方法去獲取頁面的渲染完成時間 endTime。 ### 統計頁面創建的時間 獲取頁面創建的時間比較容易,我們只需要在頁面的初始化函數 initState() 里記錄頁面的創建時間 startTime。 最后,再將這兩個時間做減法,你就能得到頁面的加載時長。 需要注意的是,**正常的頁面加載時長一般都不應該超過2秒。如果超過了,則意味著有嚴重的性能問題**。 # 三、布局加載優化 > Flutter 為什么要使用聲明書 UI 的編寫方式? 為了減輕開發人員的負擔,無需編寫如何在不同的 UI 狀態之間進行切換的代碼,Flutter 使用了聲明式的 UI 編寫方式,而不是 Android 和 iOS 中的命令式編寫方式。 這樣的話,**當用戶界面發生變化時,Flutter ?不會修改舊的 Widget 實例,而是會構造新的 Widget 實例**。 Fluuter 框架使用 RenderObjects 管理傳統 UI 對象的職責(比如維護布局的狀態)。RenderObjects 在幀之間保持不變, Flutter 的輕量級 Widget 通知框架在狀態之間修改 RenderObjects, 而 Flutter Framework 則負責處理其余部分。 ## 1、常規優化 常規優化即針對 build() 進行優化,build() 方法中的性能問題一般有兩種:**耗時操作和 Widget 堆疊**。 ### 1)、在 build() 方法中執行了耗時操作 我們應該盡量避免在 build() 中執行耗時操作,因為 build() 會被頻繁地調用,尤其是當 Widget 重建的時候。 此外,我們不要在代碼中進行阻塞式操作,可以將文件讀取、數據庫操作、網絡請求等通過 Future 來轉換成異步方式來完成。 最后,對于 CPU 計算頻繁的操作,例如圖片壓縮,可以使用 isolate 來充分利用多核心 CPU。 **isolate 作為 Flutter 中的多線程實現方式,之所以被稱之為 isolate(隔離),是因為每一個 isolate 都有一份單獨的內存**。 **Flutter 會運行一個事件循環,它會從事件隊列中取得最舊的事件,處理它,然后再返回下一個事件進行處理,依此類推,直到事件隊列清空為止。每當動作中斷時,線程就會等待下一個事件**。 實質上,不僅僅是 isolate,所有的高級 API 都能夠應用于異步編程,例如 Futures、Streams、async 和 await,它們全部都是構建在這個簡單的事件循環之上。 而,async 和 await 實際上只是使用 futures 和 streams 的替代語法,它將代碼編寫形式從異步變為同步,主要用來幫助你編寫更清晰、簡潔的代碼。 此外,async 和 await 也能使用 try on catch finally 來進行異常處理,這能夠幫助你處理一些數據解析方面的異常。 ### 2)、build() 方法中堆砌了大量的 Widget 這將會導致三個問題: * 1、**代碼可讀性差**:畫界面時需要一個 Widget 嵌套一個 Widget,但如果 Widget 嵌套太深,就會導致代碼的可讀性變差,也不利于后期的維護和擴展。 * 2、**復用難**:由于所有的代碼都在一個 build(),會導致無法將公共的 UI 代碼復用到其它的頁面或模塊。 * 3、**影響性能**:我們在 State 上調用 setState() 時,所有 build() 中的 Widget 都將被重建,因此 build() 中返回的 Widget 樹越大,那么需要重建的 Widget 就越多,也就會對性能越不利。 所以,你需要?**控制 build 方法耗時,將 Widget 拆小,避免直接返回一個巨大的 Widget,這樣 Widget 會享有更細粒度的重建和復用**。 ### 3)、使用 Widget 而不是函數 如果一個函數可以做同樣的事情,Flutter 就不會有 StatelessWidget ,使用 StatelessWidget 的最大好處在于:能盡量避免不必要的重建。總的來說,它的優勢有: * 1)、**允許性能優化:const 構造函數,更細粒度的重建等等**。 * 2)、**確保在兩個不同的布局之間切換時,能夠正確地處理資源(因為函數可能重用某些先前的狀態)**。 * 3)、**確保熱重載正常工作,使用函數可能會破壞熱重載**。 * 4)、**在 flutter 自帶的 Widget 顯示工具中能看到 Widget 的狀態和參數**。 * 5)、**發生錯誤時,有更清晰的提示:此時,Flutter 框架將為你提供當前構建的 Widget 名稱,更容易排查問題**。 * 6)、**可以定義 key 和方便使用 context 的 API**。 ### 4)、盡可能地使用 const 如果某一個實例已經用 const 定義好了,那么其它地方再次使用 const 定義時,則會直接從常量池里取,這樣便能夠節省 RAM。 ### 5)、盡可能地使用 const 構造器 當構建你自己的 Widget 或者使用 Flutter 的 Widget 時,這將會幫助 Flutter 僅僅去 rebuild 那些應當被更新的 Widget。 因此,你應該盡量多用 const 組件,這樣即使父組件更新了,子組件也不會重新進行 rebuild 操作。特別是針對一些長期不修改的組件,例如通用報錯組件和通用 loading 組件等。 ### 6)、使用 nil 去替代 Container() 和 SizedBox() 首先,你需要明白?**nil 僅僅是一個基礎的 Widget 元素 ,它的構建成本幾乎沒有**。 在某些情況下,如果你不想顯示任何內容,且不能返回 null 的時候,你可能會返回類似 const SizedBox/Container 的 Widget,但是 SizedBox 會創建 RenderObject,而渲染樹中的 RenderObject 會帶來多余的生命周期控制和額外的計算消耗,即便你沒有給 SizedBox 指定任何的參數。 下面,是我平時使用 nil 的一套方式: ~~~ //?BEST text?!=?null???Text(text)?:?nil or if?(text?!=?null)?Text(text) text?!=?null???Text(text)?:?const?Container()/SizedBox() ~~~ ### 7)、列表優化 在構建大型網格或列表的時候,我們要盡量避免使用 ListView(children: \[\],) 或 GridView(children: \[\],)。 因為,在這種場景下,不管列表內容是否可見,會導致列表中所有的數據都會被一次性繪制出來,這種用法類似于 Android 的 ScrollView。 如果我們列表數據比較大的時候,建議使用 ListView 和 GridView 的 builder 方法,它們只會繪制可見的列表內容,類似于 Android 的 RecyclerView。 其實,本質上,**就是對列表采用了懶加載而不是直接一次性創建所有的子 Widget,這樣視圖的初始化時間就減少了**。 ### 8)、針對于長列表,記得在 ListView 中使用 itemExtent。 有時候當我們有一個很長的列表,想要用滾動條來大跳時,使用 itemExtent 就很重要了,**它會幫助 Flutter 去計算 ListView 的滾動位置而不是計算每一個 Widget 的高度,與此同時,它能夠使滾動動畫有更好的性能**。 ### 9)、減少可折疊 ListView 的構建時間 **針對于可折疊的 ListView,未展開狀態時,設置其 itemCount 為 0,這樣 item 只會在展開狀態下才進行構建,以減少頁面第一次的打開構建時間**。 ### 10)、盡量不要為 Widget 設置半透明效果 考慮用圖片的形式代替,這樣被遮擋的部分 Widget 區域就不需要繪制了。 除此之外,還有網絡請求預加載優化、抽取文本 Theme 等常規的優化方式就不贅述了。 ## 2、深入優化 ### 1)、優化光柵線程 所有的 Flutter 應用至少都會運行在兩個并行的線程上:**UI 線程和 Raster 線程**。 \*\*UI 線程是你構建 Widgets 和運行應用邏輯的地方。\*\***Raster 線程是 Flutter 用來柵格化你的應用的。它從 UI 線程獲取指令并將它們轉換為可以發送到圖形卡的內容**。 **在光柵線程中,會獲取圖片的字節,調整圖像的大小,應用透明度、混合模式、模糊等等,直到產生最后的圖形像素。然后,光柵線程會將其發送到圖形卡,繼而發送到屏幕上顯示**。 使用 Flutter DevTools-Performance 進行檢測,步驟如下: * 1、在 Performance Overlay 中,查看光柵線程和 UI 線程哪個負載過重。 * 2、在 Timeline Events 中,找到那些耗費時間最長的事件,例如常見的 SkCanvas::Flush,它負責解決所有待處理的 GPU 操作。 * 3、找到對應的代碼區域,通過刪除 Widgets 或方法的方式來看對性能的影響。 ### 2)、用 key 加速 Flutter 的性能優化光柵線程 一個 element 是由 Widget 內部創建的,它的主要目的是,**知道對應的 Widget 在 Widget 樹中所處的位置。但是元素的創建是非常昂貴的,通過 Keys(ValueKeys 和 GlobalKeys),我們可以去重復使用它們**。 > GlobalKey 與 ValueKey 的區別? **GlobalKey 是全局使用的 key,在跨小部件的場景時,你就可以使用它去刷新其它小部件。但,它是很昂貴的,如果你不需要訪問 BuildContext、Element 和 State,應該盡量使用 LocalKey**。 而 ValueKey 和 ObjectKey、UniqueKey 一樣都歸屬于局部使用的 LocalKey,無法跨容器使用,ValueKey 比較的是 Widget 的值,而 ObjectKey 比較的是對象的 key,UniqueKey 則每次都會生成一個不同的值。 #### 元素的生命周期 * **Mount**:掛載,當元素第一次被添加到樹上的時候調用。 * **Active**:當需要激活之前失活的元素時被調用。 * **Update**:用新數據去更新 RenderObject。 * **Deactive**:當元素從 Widget 樹中被移除或移動時被調用。如果一個元素在同一幀期間被移動了且它有 GlobalKey,那么它仍然能夠被激活。 * **UnMount**:卸載,如果一個元素在一幀期間沒有被激活,它將會被卸載,并且再也不會被復用。 #### 優化方式 **為了去改善性能,你需要去盡可能讓 Widget 使用 Activie 和 Update 操作,并且盡量避免讓 Widget觸發 UnMount 和 Mount**。而使用 GlobayKeys 和 ValueKey 則能做到這一點: ~~~ ///?1、給?MaterialApp?指定?GlobalKeys MaterialApp(key:?global,?home:?child,); ///?2、通過把?ValueKey?分配到正在被卸載的根?Widget,你就能夠 ///?減少 Widget 的平均構建時間。 Widget?build(BuildContext?context)?{ ??return?Column( ????children:?[ ??????value ????????????const?SizedBox(key:?ValueKey('SizedBox')) ??????????:?const?Placeholder(key:?ValueKey('Placeholder')), ??????GestureDetector( ????????key:?ValueKey('GestureDetector'), ????????onTap:?()?{ ??????????setState(()?{ ????????????value?=?!value; ??????????}); ????????}, ????????child:?Container( ??????????width:?100, ??????????height:?100, ??????????color:?Colors.red, ????????), ??????), ??????!value ????????????const?SizedBox(key:?ValueKey('SizedBox')) ??????????:?const?Placeholder(key:?ValueKey('Placeholder')), ????], ??); } ~~~ > 如何知道哪些 Widget 會被 Update,哪些 Widget會被 UnMount? 只有 build 直接 return 的那個根 Widget 會自動更新,其它都有可能被 UnMount,因此都需要給其分配 ValueKey。 > 為什么沒有給 Container 分配 ValueKey? 因為 Container 是 GestureDetector 的一個子 Widget,所以當給 GestureDetector 使用 ValueKey 去實現復用更新時,Container 也能被自動更新。 #### 優化效果 優化前: ![圖片](data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==) 優化后: ![圖片](data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==) 可以看到,平均構建時間?**由 5.5ms 減少到 1.6ms**,優化效果還是很明顯的。 #### 優勢 大幅度減少 Widget的平均構建時間。 #### 缺點 * **過多使用 ValueKey 會讓你的代碼變得更冗余**。 * **如果你的根 Widget 是 MaterialApp 時,則需要使用 GlobalKey,但當你去重復使用 GlobalKey 時可能會導致一些錯誤,所以一定要避免濫用 Key**。 注意??:在大部分場景下,Flutter 的性能都是足夠的,不需要這么細致的優化,只有當產生了視覺上的問題,例如卡頓時才需要去分析優化。 # 四、啟動速度優化 ## 1、Flutter 引擎預加載 使用它可以達到頁面秒開的一個效果,具體實現為: 在 HIFlutterCacheManager 類中定義一個 preLoad 方法,**使用 Looper.myQueue().addIdleHandler 添加一個 idelHandler,當 CPU 空閑時會回調 queueIdle 方法,在這個方法里,你就可以去初始化 FlutterEngine,并把它緩存到集合中**。 預加載完成之后,你就可以通過 HIFlutterCacheManager 類的 getCachedFlutterEngine 方法從集合中獲取到緩存好的引擎。 ## 2、Dart VM 預熱 對于 Native + Flutter 的混合場景,如果不想使用引擎預加載的方式,那么要提升 Flutter 的啟動速度也可以通 過Dart VM 預熱來完成,這種方式會提升一定的 Flutter 引擎加載速度,但整體對啟動速度的提升沒有預加載引擎提升的那么多。 ![圖片](data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==) **無論是引擎預加載還是 Dart VM 預熱都是有一定的內存成本的,如果 App 內存壓力不大,并且預判用戶接下來會訪問 Flutter 業務,那么使用這個優化就能帶來很好的價值;反之,則可能造成資源浪費,意義不大**。 # 五、內存優化 ## 1、const 實例化 ### 優勢 **const 對象只會創建一個編譯時的常量值。在代碼被加載進 Dart Vm 時,在編譯時會存儲在一個特殊的查詢表里,由于 flutter 采用了 AoT 編譯,const + values 的方式會提供一些小的性能優勢**。例如:const Color() 僅僅只分配一次內存給當前實例。 ### 應用場景 Color()、GlobayKey() 等等。 ## 2、識別出消耗多余內存的圖片 Flutter Inspector:**點擊 “Invert Oversized Images”,它會識別出那些解碼大小超過展示大小的圖片,并且系統會將其倒置,這些你就能更容易在 App 頁面中找到它**。 ![圖片](https://mmbiz.qpic.cn/mmbiz_png/PjzmrzN77aCOvWZZvDka4Zz7ZBSzVFA6FxBZumXDSfTNRkKUhZeCz92jqkAQ5rwibEOGeg5YAsDibaKEPpCUT4WA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 針對這些圖片,你可以指定 cacheWidth 和 cacheHeight 為展示大小,這樣可以讓 flutter 引擎以指定大小解析圖片,減少內存消耗。 ## 3、針對 ListView item 中有 image 的情況來優化內存 ListView 不能夠殺死那些在屏幕可視范圍之外的那些 item,如果 item 使用了高分辨率的圖片,那么它將會消耗非常多的內存。 換言之,ListView 在默認情況下會在整個滑動/不滑動的過程中讓子 Widget 保持活動狀態,這一點是通過 AutomaticKeepAlive 來保證,在默認情況下,每個子 Widget 都會被這個 Widget 包裹,以使被包裹的子 Widget 保持活躍。 其次,如果用戶向后滾動,則不會再次重新繪制子 Widget,這一點是通過 RepaintBoundaries 來保證,在默認情況下,每個子 Widget 都會被這個 Widget 包裹,它會讓被包裹的子 Widget 僅僅繪制一次,以此獲得更高的性能。 但,這樣的問題在于,如果加載大量的圖片,則會消耗大量的內存,最終可能使 App 崩潰。 ### 解決方案 **通過將這兩個選項置為 false 來禁用它們,這樣不可見的子元素就會被自動處理和 GC**。 ~~~ ListView.builder( ??... ??addAutomaticKeepAlives:?false?(true?by?default) ??addRepaintBoundaries:?false?(true?by?default) ); ~~~ **由于重新繪制子元素和管理狀態等操作會占用更多的 CPU 和 GPU 資源,但是它能夠解決你 App 的內存問題,并且會得到一個高性能的視圖列表**。 # 六、包體積優化 ## 1、圖片優化 對圖片壓縮或使用在線的網絡圖片。 ## 2、移除冗余的二三庫 隨著業務的增加,項目中會引入越來越多的二三方庫,其中有不少是功能重復的,甚至是已經不再使用的。移除不再使用的和將相同功能的庫進行合并可以進一步減少包體積。 ## 3、啟用代碼縮減和資源縮減 打開 minifyEnabled 和 shrinkResources,構建出來的 release 包會減少 10% 左右的大小,甚至更多。 ## 4、構建單 ABI 架構的包 目前手機市場上,x86 / x86\_64/armeabi/mips / mips6 的占有量很少,arm64-v8a 作為最新一代架構,是目前的主流,而 armeabi-v7a 只存在少部分的老舊手機中。 所以,**為了進一步優化包大小,你可以構建出單一架構的安裝包,在 Flutter 中可以通過以下方式來構建出單一架構的安裝包**: ~~~ cd?<flutter應用的android目錄> flutter?build?apk?--split-per-abi ~~~ 如果想進一步壓縮包體積可將 so 進行動態下發,將 so 放在遠端進行動態加載,不僅能進一步減少包體積也可以實現代碼的熱修復和動態加載。 # 七、總結 在本篇文章中,我主要從以下 六個方面 講解了 Flutter 性能優化相關的知識: * 1)、**檢測手段**:Flutter Inspector、性能圖層、Raster 和 UI 線程問題的定位 使用 checkerboardOffscreenLayers 檢查多視圖疊加的視圖渲染 、使用 checkerboardRasterCacheImages 檢查緩存的圖像。 * 2)、**關鍵優化指標**:包括頁面異常率、頁面幀率、頁面加載時長。 * 3)、**布局加載優化**:十大常規優化、優化光柵線程、用 key 加速 Flutter 的性能。 * 4)、**啟動速度優化**:引擎預加載和 Dart VM 預熱。 * 5)、**內存優化**:const 實例化、識別出消耗多余內存的圖片、針對 ListView item 中有 image 的情況來優化內存。 * 6)、**包體積優化**:圖片優化、移除冗余的二三庫、啟用代碼縮減和資源縮減、構建單 ABI 架構的包。 在近一年實踐 Flutter 的過程中,越發發現一個人真正應該具備的核心能力應該是你的思考能力。 思考能力,包括?**結構化思考/系統性思考/遷移思考/層級思考/逆向思考/多元思考**?等,使用這些思考能力分析 # 參考資料 [深入探索 Flutter 性能優化](https://mp.weixin.qq.com/s/pUNlOXGfYo4taFteOg0SxA)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看