<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>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                [TOC] ## UI 渲染測量 ### 手動工具 * 測試工具:Profile GPU Rendering 和 Show GPU Overdraw,具體的使用方法你可以參考[《檢查 GPU 渲染速度和繪制過度》](https://developer.android.com/studio/profile/inspect-gpu-rendering)。 * 問題定位工具:Systrace 和 Tracer for OpenGL ES,具體使用方法可以參考[《Slow rendering》](https://developer.android.com/topic/performance/vitals/render)。 在 Android Studio 3.1 之后,Android 推薦使用[Graphics API Debugger(GAPID)](https://github.com/google/gapid)來替代 Tracer for OpenGL ES 工具。GAPID 可以說是升級版,它不僅可以跨平臺,而且功能更加強大,支持 Vulkan 與回放。 ![ui_2_1](https://blog.yorek.xyz/assets/images/android/master/ui_2_1.png) 通過上面的幾個工具,我們可以初步判斷應用 UI 渲染的性能是否達標,例如是否經常出現掉幀、掉幀主要發生在渲染的哪一個階段、是否存在 Overdraw 等。 雖然這些圖形化界面工具非常好用,但是它們難以用在自動化測試場景中,那有哪些測量方法可以用于自動化測量 UI 渲染性能呢? ### gfxinfo [gfxinfo](https://developer.android.com/training/testing/performance)可以輸出包含各階段發生的動畫以及幀相關的性能信息,具體命令如下: ~~~ adb shell dumpsys gfxinfo 包名 ~~~ 除了渲染的性能之外,gfxinfo 還可以拿到渲染相關的內存和 View hierarchy 信息。在 Android 6.0 之后,gxfinfo 命令新增了 framestats 參數,可以拿到最近 120 幀每個繪制階段的耗時信息。 ~~~ adb shell dumpsys gfxinfo 包名 framestats ~~~ 通過這個命令我們可以實現自動化統計應用的幀率,更進一步還可以實現自定義的“Profile GPU Rendering”工具,在出現掉幀的時候,自動統計分析是哪個階段的耗時增長最快,同時給出相應的[建議](https://developer.android.com/topic/performance/rendering/profile-gpu)。 ![ui_2_2](https://blog.yorek.xyz/assets/images/android/master/ui_2_2.png) ### SurfaceFlinger 除了耗時,我們還比較關心渲染使用的內存。上一期我講過,在 Android 4.1 以后每個 Surface 都會有三個 Graphic Buffer,那如何查看 Graphic Buffer 占用的內存,系統是怎么樣管理這部分的內存的呢? 你可以通過下面的命令拿到系統 SurfaceFlinger 相關的信息: ~~~ adb shell dumpsys SurfaceFlinger ~~~ 下面以今日頭條為例,應用使用了三個 Graphic Buffer 緩沖區,當前用在顯示的第二個 Graphic Buffer,大小是 1080 x 1920。現在我們也可以更好地理解三緩沖機制,你可以看到這三個 Graphic Buffer 的確是在交替使用。 ~~~ + Layer 0x793c9d0c00 (com.ss.***。news/com.**.MainActivity) //序號 //狀態 //對象 //大小 >[02:0x794080f600] state=ACQUIRED, 0x794081bba0 [1080x1920:1088, 1] [00:0x793e76ca00] state=FREE , 0x793c8a2640 [1080x1920:1088, 1] [01:0x793e76c800] state=FREE , 0x793c9ebf60 [1080x1920:1088, 1] ~~~ 繼續往下看,你可以看到這三個 Buffer 分別占用的內存: ~~~ Allocated buffers: 0x793c8a2640: 8160.00 KiB | 1080 (1088) x 1920 | 1 | 0x20000900 0x793c9ebf60: 8160.00 KiB | 1080 (1088) x 1920 | 1 | 0x20000900 0x794081bba0: 8160.00 KiB | 1080 (1088) x 1920 | 1 | 0x20000900 ~~~ 這部分的內存其實真的不小,特別是現在手機的分辨率越來越大,而且還很多情況應用會有其他的 Surface 存在,例如使用了SurfaceView或者TextureView等。 那系統是怎么樣管理這部分內存的呢?當應用退到后臺的時候,系統會將這些內存回收,也就不會再把它們計算到應用的內存占用中。 ~~~ + Layer 0x793c9d0c00 (com.ss.***。news/com.**.MainActivity) [00:0x0] state=FREE [01:0x0] state=FREE [02:0x0] state=FREE ~~~ 那么如何快速地判別 UI 實現是否符合設計稿?如何更高效地實現 UI 自動化測試?這些問題你可以先思考一下,我們將在后面“高效測試”中再詳細展開。 ## UI 優化的常用手段 讓我們再重溫一下 UI 渲染的階段流程圖,我們的目標是實現 60 fps,這意味著渲染的所有操作都必須在 16 ms(= 1000 ms/60 fps)內完成。 ![](https://img.kancloud.cn/33/28/33285d39f5f4d50be26a403603b8867e_1920x1078.png) 所謂的 UI 優化,就是拆解渲染的各個階段的耗時,找到瓶頸的地方,再加以優化。接下來我們一起來看看 UI 優化的一些常用的手段。 ### 1\. 盡量使用硬件加速 通過上一期學習,相信你也發自內心地認同硬件加速繪制的性能是遠遠高于軟件繪制的。所以說 UI 優化的第一個手段就是保證渲染盡量使用硬件加速。 有哪些情況我們不能使用硬件加速呢?之所以不能使用硬件加速,是因為硬件加速不能支持所有的 Canvas API,具體 API 兼容列表可以見[drawing-support](https://developer.android.com/guide/topics/graphics/hardware-accel#drawing-support)文檔。如果使用了不支持的 API,系統就需要通過 CPU 軟件模擬繪制,這也是漸變、磨砂、圓角等效果渲染性能比較低的原因。 SVG 也是一個非常典型的例子,SVG 有很多指令硬件加速都不支持。但我們可以用一個取巧的方法,提前將這些 SVG 轉換成 Bitmap 緩存起來,這樣系統就可以更好地使用硬件加速繪制。同理,對于其他圓角、漸變等場景,我們也可以改為 Bitmap 實現。 這種取巧方法實現的關鍵在于如何提前生成 Bitmap,以及 Bitmap 的內存需要如何管理。你可以參考一下市面常用的圖片庫實現。 ### 2\. Create View 優化 觀察渲染的流水線時,有沒有同學發現缺少一個非常重要的環節,那就是 View 創建的耗時。請不要忘記,View 的創建也是在 UI 線程里,對于一些非常復雜的界面,這部分的耗時不容忽視。 在優化之前我們先來分解一下 View 創建的耗時,可能會包括各種 XML 的隨機讀的 I/O 時間、解析 XML 的時間、生成對象的時間(Framework 會大量使用到反射)。 相應的,我們來看看這個階段有哪些優化方式。 #### 使用代碼創建 使用 XML 進行 UI 編寫可以說是十分方便,可以在 Android Studio 中實時預覽到界面。如果我們要對一個界面進行極致優化,就可以使用代碼進行編寫界面。 但是這種方式對開發效率來說簡直是災難,因此我們可以使用一些開源的 XML 轉換為 Java 代碼的工具,例如[X2C](https://github.com/iReaderAndroid/X2C)。但坦白說,還是有不少情況是不支持直接轉換的。 所以我們需要兼容性能與開發效率,我建議只在對性能要求非常高,但修改又不非常頻繁的場景才使用這個方式。 #### 異步創建 那我們能不能在線程提前創建 View,實現 UI 的預加載嗎?嘗試過的同學都會發現系統會拋出下面這個異常: ~~~ java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() at android.os.Handler.<init>(Handler.java:121) ~~~ 事實上,我們可以通過又一個非常取巧的方式來實現。在使用線程創建 UI 的時候,先把線程的 Looper 的 MessageQueue 替換成 UI 線程 Looper 的 Queue。 ![ui_2_4](https://blog.yorek.xyz/assets/images/android/master/ui_2_4.png) 不過需要注意的是,在創建完 View 后我們需要把線程的 Looper 恢復成原來的。 #### View 重用 正常來說,View 會隨著 Activity 的銷毀而同時銷毀。ListView、RecycleView 通過 View 的緩存與重用大大地提升渲染性能。因此我們可以參考它們的思想,實現一套可以在不同 Activity 或者 Fragment 使用的 View 緩存機制。 但是這里需要保證所有進入緩存池的 View 都已經“凈身出戶”,不會保留之前的狀態。微信曾經就因為這個緩存,導致出現不同的用戶聊天記錄錯亂。 ![ui_2_5](https://blog.yorek.xyz/assets/images/android/master/ui_2_5.png) ### 3\. measure/layout 優化 渲染流程中 measure 和 layout 也是需要 CPU 在主線程執行的,對于這塊內容網上有很多優化的文章,一般的常規方法有: * **減少 UI 布局層次**。例如盡量扁平化,使用 等優化。 * **優化 layout 的開銷**。盡量不使用 RelativeLayout 或者基于 weighted LinearLayout,它們 layout 的開銷非常巨大。這里我推薦使用 ConstraintLayout 替代 RelativeLayout 或者 weighted LinearLayout。 * **背景優化**。盡量不要重復去設置背景,這里需要注意的是主題背景(theme), theme 默認會是一個純色背景,如果我們自定義了界面的背景,那么主題的背景我們來說是無用的。但是由于主題背景是設置在 DecorView 中,所以這里會帶來重復繪制,也會帶來繪制性能損耗。 對于 measure 和 layout,我們能不能像 Create View 一樣實現線程的預布局呢?這樣可以大大地提升首次顯示的性能。 Textview 是系統控件中非常強大也非常重要的一個控件,強大的背后就代表著需要做很多計算。在 2018 年的 Google I/O 大會,發布了[PrecomputedText](https://developer.android.com/reference/android/text/PrecomputedText)并已經集成在 Jetpack 中,它給我們提供了接口,可以異步進行 measure 和 layout,不必在主線程中執行。 ## UI 優化的進階手段 那對于其他的控件我們是不是也可以采用相同的方式?接下來我們一起來看看近兩年新框架的做法,我來介紹一下 Facebook 的一個開源庫 Litho 以及 Google 開源的 Flutter。 ### 1\. Litho:異步布局 [Litho](https://github.com/facebook/litho)是 Facebook 開源的聲明式 Android UI 渲染框架,它是基于另外一個 Facebook 開源的布局引擎[Yoga](https://github.com/facebook/yoga)開發的。 Litho 本身非常強大,內部做了很多非常不錯的優化。下面我來簡單介紹一下它是如何優化 UI 的。 #### 異步布局 一般來說的 Android 所有的控件繪制都要遵守 measure -> layout -> draw 的流水線,并且這些都發生在主線程中。 ![ui_2_6](https://blog.yorek.xyz/assets/images/android/master/ui_2_6.png) Litho 如我前面提到的 PrecomputedText 一樣,把 measure 和 layout 都放到了后臺線程,只留下了必須要在主線程完成的 draw,這大大降低了 UI 線程的負載。它的渲染流水線如下: ![ui_2_7](https://blog.yorek.xyz/assets/images/android/master/ui_2_7.png) #### 界面扁平化 前面也提到過,降低 UI 的層級是一個非常通用的優化方法。你肯定會想,有沒有一種方法可以直接降低 UI 的層級,而不通過代碼的改變呢?Litho 就給了我們一種方案,由于 Litho 使用了自有的布局引擎(Yoga),在布局階段就可以檢測不必要的層級、減少 ViewGroups,來實現 UI 扁平化。比如下面這樣圖,上半部分是我們一般編寫這個界面的方法,下半部分是 Litho 編寫的界面,可以看到只有一層層級。 ![ui_2_8](https://blog.yorek.xyz/assets/images/android/master/ui_2_8.png) #### 優化 RecyclerView Litho 還優化了 RecyclerView 中 UI 組件的緩存和回收方法。原生的 RecyclerView 或者 ListView 是按照 viewType 來進行緩存和回收,但如果一個 RecyclerView/ListView 中出現 viewType 過多,會使緩存形同虛設。但 Litho 是按照 text、image 和 video 獨立回收的,這可以提高緩存命中率、降低內存使用率、提高滾動幀率。 ![ui_2_9](https://blog.yorek.xyz/assets/images/android/master/ui_2_9.png) Litho 雖然強大,但也有自己的缺點。它為了實現 measure/layout 異步化,使用了類似 react 單向數據流設計,這一定程度上加大了 UI 開發的復雜性。并且 Litho 的 UI 代碼是使用 Java/Kotlin 來進行編寫,無法做到在 AS 中預覽。 如果你沒有計劃完全遷移到 Litho,我建議可以優先使用 Litho 中的 RecyclerCollectionComponent 和 Sections 來優化自己的 RecyelerView 的性能。 ### 2\. Flutter:自己的布局 + 渲染引擎 如下圖所示,Litho 雖然通過使用自己的布局引擎 Yoga,一定程度上突破了系統的一些限制,但是在 draw 之后依然走的系統的渲染機制。 ![ui_2_10](https://blog.yorek.xyz/assets/images/android/master/ui_2_10.png) 那我們能不能再往底層深入,把系統的渲染也同時接管過來?Flutter 正是這樣的框架,它也是最近十分火爆的一個新框架,這里我也簡單介紹一下。 Flutter是 Google 推出并開源的移動應用開發框架,開發者可以通過 Dart 語言開發 App,一套代碼同時運行在 iOS 和 Android 平臺。 我們先整體看一下 Flutter 的架構,在 Android 上 Flutter 完全沒有基于系統的渲染引擎,而是把 Skia 引擎直接集成進了 App 中,這使得 Flutter App 就像一個游戲 App。并且直接使用了 Dart 虛擬機,可以說是一套跳脫出 Android 的方案,所以 Flutter 也可以很容易實現跨平臺。 ![ui_2_11](https://blog.yorek.xyz/assets/images/android/master/ui_2_11.png) 開發 Flutter 應用總的來說簡化了線程模型,框架給我們抽象出各司其職的 Runner,包括 UI、GPU、I/O、Platform Runner。Android 平臺上面每一個引擎實例啟動的時候會為 UI Runner、GPU Runner、I/O Runner 各自創建一個新的線程,所有 Engine 實例共享同一個 Platform Runner 和線程。由于本期我們主要討論 UI 渲染相關的 由于本期我們主要討論 UI 渲染相關的內容,我來著重分析一下 Flutter 的渲染步驟,相關的具體知識你可以閱讀[《Flutter 原理與實踐》](https://tech.meituan.com/2018/08/09/waimai-flutter-practice.html)。 * 首先 UI Runner 會執行 root isolate(可以簡單理解為 main 函數。需要簡單解釋一下 isolate 的概念,isolate 是 Dart 虛擬機中一種執行并發代碼實現,Dart 虛擬機實現了 Actor 的并發模型,與大名鼎鼎的 Erlang 使用了類似的并發模型。如果不太了解 Actor 的同學,可以簡單認為 isolate 就是 Dart 虛擬機的“線程”,Root isolate 會通知引擎有幀要渲染)。 * Flutter 引擎得到通知后,會告知系統我們要同步 VSYNC。 * 得到 GPU 的 VSYNC 信號后,對 UI Widgets 進行 Layout 并生成一個 Layer Tree。 * 然后 Layer Tree 會交給 GPU Runner 進行合成和柵格化。 * GPU Runner 使用 Skia 庫繪制相關圖形。 ![ui_2_12](https://blog.yorek.xyz/assets/images/android/master/ui_2_12.png) Flutter 也采用了類似 Litho、React 屬性不可變,單向數據流的方案。這已經成為現代 UI 渲染引擎的標配。這樣做的好處是可以將視圖與數據分離。 總體來說 Flutter 吸取和各個優秀前端框架的精華,還“加持”了強大的 Dart 虛擬機和 Skia 渲染引擎,可以說是一個非常優秀的框架,閑魚、今日頭條等很多應用部分功能已經使用 Flutter 開發。結合 Google 最新的 Fuchsia 操作系統,它會不會是一個顛覆 Android 的開發框架?我們在專欄后面會單獨詳細討論 Flutter。 ### 3\. RenderThread 與 RenderScript[?](https://blog.yorek.xyz/android/paid/master/ui_2/#3-renderthread-renderscript "Permanent link") 在 Android 5.0,系統增加了 RenderThread,對于 ViewPropertyAnimator 和 CircularReveal 動畫,我們可以使用[RenderThead 實現動畫的異步渲染](https://mp.weixin.qq.com/s/o-e0MvrJbVS_0HHHRf43zQ)。當主線程阻塞的時候,普通動畫會出現明顯的丟幀卡頓,而使用 RenderThread 渲染的動畫即使阻塞了主線程仍不受影響。 現在越來越多的應用會使用一些高級圖片或者視頻編輯功能,例如圖片的高斯模糊、放大、銳化等。拿日常我們使用最多的“掃一掃”這個場景來看,這里涉及大量的圖片變換操作,例如縮放、裁剪、二值化以及降噪等。 圖片的變換涉及大量的計算任務,而根據我們上一期的學習,這個時候使用 GPU 是更好的選擇。那如何進一步壓榨系統 GPU 的性能呢? 我們可以通過[RenderScript](https://developer.android.com/guide/topics/renderscript/compute),它是 Android 操作系統上的一套 API。它基于異構計算思想,專門用于密集型計算。RenderScript 提供了三個基本工具:一個硬件無關的通用計算 API;一個類似于 CUDA、OpenCL 和 GLSL 的計算 API;一個類[C99](https://zh.wikipedia.org/wiki/C99)的腳本語言。允許開發者以較少的代碼實現功能復雜且性能優越的應用程序。 如何將它們應用到我們的項目中?你可以參考下面的一些實踐方案: * [RenderScript 渲染利器](https://www.jianshu.com/p/b72da42e1463) * [RenderScript : 簡單而快速的圖像處理](https://dnspod.qcloud.com/static/webblock.html?d=www.jcodecraeer.com) * [Android RenderScript 簡單高效實現圖片的高斯模糊效果](http://yifeng.studio/2016/10/20/android-renderscript-blur/) ## 總結[?](https://blog.yorek.xyz/android/paid/master/ui_2/#_5 "Permanent link") 回顧一下 UI 優化的所有手段,我們會發現它存在這樣一個脈絡: 1. **在系統的框架下優化**。布局優化、使用代碼創建、View 緩存等都是這個思路,我們希望減少甚至省下渲染流水線里某個階段的耗時。 2. **利用系統新的特性**。使用硬件加速、RenderThread、RenderScript 都是這個思路,通過系統一些新的特性,最大限度壓榨出性能。 3. **突破系統的限制**。由于 Android 系統碎片化非常嚴重,很多好的特性可能低版本系統并不支持。而且系統需要支持所有的場景,在一些特定場景下它無法實現最優解。這個時候,我們希望可以突破系統的條條框框,例如 Litho 突破了布局,Flutter 則更進一步,把渲染也接管過來了。 # 參考資料 [# UI 優化(下):如何優化 UI 渲染?](https://blog.yorek.xyz/android/paid/master/ui_2/)
                  <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>

                              哎呀哎呀视频在线观看