Android 卡頓最佳化 1 卡頓解析

來源:互聯網
上載者:User

標籤:ui設計   str   vmpolicy   內容   link   gen   ebe   enc   相對   

1, 感知卡頓

使用者對卡頓的感知, 主要來源於介面的重新整理. 而介面的效能主要是依賴於裝置的UI渲染效能. 如果我們的UI設計過於複雜, 或是實現不夠好, 裝置又不給力, 介面就會像卡住了一樣, 給使用者卡頓的感覺.

1.1 16ms原則

在剖析卡頓的原因之前, 我們先來瞭解下Android中著名的"16ms"原則:

Android系統每隔16ms會發出VSYNC訊號重繪我們的介面(Activity).
為什麼是16ms, 因為Android設定的重新整理率是60FPS(Frame Per Second), 也就是每秒60幀的重新整理率, 約合16ms重新整理一次.

就像是這樣的:

16ms

這就意味著, 我們需要在16ms內完成下一次要重新整理的介面的相關運算, 以便介面重新整理更新. 然而, 如果我們無法在16ms內完成此次運算會怎樣呢?

例如, 假設我們更新螢幕的背景圖片, 需要24ms來做這次運算. 當系統在第一個16ms時重新整理介面, 然而我們的運算還沒有結束, 無法繪出圖片. 當系統隔16ms再發一次VSYNC資訊重繪介面時, 使用者才會看到更新後的圖片. 也就是說使用者是32ms後看到了這次重新整理(注意, 並不是24ms). 這就是傳說中的丟幀(dropped frame):


dropped frame

丟幀給使用者的感覺就是卡頓, 而且如果運算過於複雜, 丟幀會更多, 導致介面常常處於停滯狀態, 卡到爆.

那麼會有哪些常見的情況會導致運算超過16ms, 進而丟幀, 讓使用者覺得卡頓呢?

2, 卡頓原因分析及處理

一般來說, 會有以下幾種情況導致卡頓這種效能問題, 我們逐一看下:

2.1 過於複雜的布局

上節有說, 介面效能取決於UI渲染效能. 我們可以理解為UI渲染的整個過程是由CPU和GPU兩個部分協同完成的.

其中, CPU負責UI布局元素的Measure, Layout, Draw等相關運算執行. GPU負責柵格化(rasterization), 將UI元素繪製到螢幕上.

如果我們的UI布局層次太深, 或是自訂控制項的onDraw中有複雜運算, CPU的相關運算就可能大於16ms, 導致卡頓.

這個時候, 我們需要藉助Hierarchy Viewer這個工具來幫我們分析布局了. Hierarchy Viewer不僅可以以圖形化樹狀結構的形式展示出UI層級, 還對每個節點給出了三個小圓點, 以指示該元素Measure, Layout, Draw的耗時及效能.

具體請參考App最佳化之Layout怎麼擺.

2.2 過度繪製(Overdraw)

上節說的CPU方面的, 關於GPU的繪製, 如果我們的介面存在Overdraw, 也可能導致卡頓.

Overdraw: 用來描述一個像素在螢幕上多少次被重繪在一幀上.
通俗的說: 理想情況下, 每屏每幀上, 每個像素點應該只被繪製一次, 如果有多次繪製, 就是Overdraw, 過度繪製了.

2.2.1 調試Overdraw

Android系統提供了可視化的方案來讓我們很方便的查看overdraw的現象:
在"系統設定"-->"開發人員選項"-->"調試GPU過度繪製"中開啟調試:

toggle GPU overdraw

此時介面可能會有五種顏色標識:

overdraw indicator
  • 原色: 沒有overdraw
  • 藍色: 1次overdraw
  • 綠色: 2次overdraw
  • 粉色: 3次overdraw
  • 紅色: 4次及4次以上的overdraw

一般來說, 藍色是可接受的, 是效能優的.

2.2.2 Overdraw的分析處理

上面有言, 所謂Overdraw, 就是在一個像素點上繪製了多次. 常見的就是:

  1. 繪製了多重背景.
  2. 繪製了不可見的UI元素.

還是以GithubApp這個App的代碼為例調試, 開啟應用, 展示是這樣的:

example-1

可以看到是中間列表這塊overdraw比較嚴重. 查看代碼發現:

fragment_trending_container.xml中ViewPager設定了背景:

<android.support.v4.view.ViewPager    android:id="@+id/view_pager"    android:background="@color/md_white_1000"    android:layout_width="match_parent"    android:layout_height="match_parent"/>

而ViewPager中的fragment又設定了背景:

<?xml version="1.0" encoding="utf-8"?><android.support.v4.widget.SwipeRefreshLayout    xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/refresh_layout"    android:background="@color/md_white_1000"    android:layout_width="match_parent"    android:layout_height="match_parent">    ...</android.support.v4.widget.SwipeRefreshLayout

完整代碼請查看Github上源碼, 本文分析時commit截止到b01b5793.

刪除外層ViewPager的背景再看:

example-2

可以發現中間列表地區已經不再是紅色了, 但是也沒有達到藍色這個可以接受的層級. 這是因為我們的Activity預設情況下, theme會給window設定一個純色的背景. 因為我們這裡不想使用這個預設的背景,故而給layout加了一層背景, 導致了多重繪製背景.

當然我們也可以自訂佈景主題, 將theme的window background設定成我們想要的, 而不在布局中設定.

可以通過如下方式去掉window的背景.

設定主題:

<item name="android:windowBackground">@null</item>

或是代碼設定, 在onCreate中:

getWindow().setBackgroundDrawable(null);

此時我們看到的效果:

example-3

已基本達到最佳化水平.

以上旨在提供分析方法和思路.
Overdraw主要原因是背景的多重繪製, 或是不可見的View在背後繪製等, 但不僅限於此.

2.3 UI線程的複雜運算

如上文ANR相關分析中就說到UI線程的複雜運算會造成UI無響應, 當然更多的是造成UI響應停滯, 卡頓.

產生ANR已經是卡頓的極致了, 具體分析可以參看App最佳化之ANR詳解一文.

關於運算阻塞導致的卡頓的分析, 可以使用Traceview這個工具.
具體Traceview的介紹, 以及實戰分析, 可以參考App最佳化之提升你的App啟動速度之理論基礎和App最佳化之提升你的App啟動速度之執行個體挑戰.

在這裡需要提下我們在效能分析工具中提到的StrictMode.

2.3.1 StrictMode的使用

StrictMode用來基於線程或VM設定一些策略, 一旦檢測到策略違例, 控制台將輸出一些警告,包含一個trace資訊展示你的應用在何處出現問題.

通常用來檢測主線程中的磁碟讀寫或網路訪問等耗時操作.

在Application或是Activity的onCreate中開啟StrictMode:

 public void onCreate() {     if (BuildConfig.DEBUG) {         // 針對線程的相關策略         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()                 .detectDiskReads()                 .detectDiskWrites()                 .detectNetwork()   // or .detectAll() for all detectable problems                 .penaltyLog()                 .build());                          // 針對VM的相關策略         StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()                 .detectLeakedSqlLiteObjects()                 .detectLeakedClosableObjects()                 .penaltyLog()                 .penaltyDeath()                 .build());     }     super.onCreate(); }

如果你的線程出了問題, 控制台會有警告輸出, 可以定位到代碼.
相對簡單, 在此就不多廢話了.
解決UI線程的耗時操作方案, 可以參考ANR詳解裡面說到的那些線程模式.

2.4 頻繁的GC

上面說的都是處理上的, CPU, GPU相關的. 實際上記憶體原因也可能會造成應用不流暢, 卡頓的.

說到這, 想起當年配台式機的三大件(CPU, 記憶體, 顯示器)了. 貌似分析App效能也是這幾大件啊 :)

為什麼說頻繁的GC會導致卡頓呢?
簡而言之, 就是執行GC操作的時候,任何線程的任何操作都會需要暫停,等待GC操作完成之後,其他動作才能夠繼續運行, 故而如果程式頻繁GC, 自然會導致介面卡頓.

以下內容參考自Android Performance Patterns:Memory Churn and Performance. 需FQ

導致頻繁GC有兩個原因:

  • 記憶體抖動(Memory Churn), 即大量的對象被建立又在短時間內馬上被釋放.
  • 瞬間產生大量的對象會嚴重佔用Young Generation的記憶體地區, 當達到閥值, 剩餘空間不夠的時候, 也會觸發GC. 即使每次分配的對象需要佔用很少的記憶體,但是他們疊加在一起會增加Heap的壓力, 從而觸發更多的GC.

這些GC操作可能會造成上面說到的丟幀, 如下:

gc dropped frame

就會讓使用者感知到卡頓了.

一般來說瞬間大量產生對象一般是因為我們在代碼的迴圈中new對象, 或是在onDraw中建立對象等. 所以說這些地方是我們尤其需要注意的...

Android 卡頓最佳化 1 卡頓解析

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.