Android界面卡頓

總要說兩句

前段時間在開發項目時搞了一下過渡繪製的問題,一個比較複雜的界面剛開發出來,過渡繪製95%紅,調了半天后,基本只有5%的紅,效果不錯,準備找個時間總結一下,今天剛好看到網上以爲大神發了一篇文章,總結的不錯,順手牽過來啦,哈哈哈~

1、感知卡頓

用戶對卡頓的感知, 主要來源於界面的刷新. 而界面的性能主要是依賴於設備的UI渲染性能. 如果我們的UI設計過於複雜, 或是實現不夠好, 設備又不給力, 界面就會像卡住了一樣, 給用戶卡頓的感覺.

1.1 16ms原則

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

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

這裏寫圖片描述

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

例如, 假設我們更新屏幕的背景圖片, 需要24ms來做這次運算. 當系統在第一個16ms時刷新界面, 然而我們的運算還沒有結束, 無法繪出圖片. 當系統隔16ms再發一次VSYNC信息重繪界面時, 用戶纔會看到更新後的圖片. 也就是說用戶是32ms後看到了這次刷新(注意, 並不是24ms). 這就是傳說中的丟幀(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的耗時及性能.

2.2 過度繪製(Overdraw)

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

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

2.2.1 調試Overdraw

Android系統提供了可視化的方案來讓我們很方便的查看overdraw的現象:
在”系統設置”–>”開發者選項”–>”調試GPU過度繪製”中開啓調試:

這裏寫圖片描述

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

這裏寫圖片描述

  • 原色: 沒有overdraw
  • 藍色: 1次overdraw
  • 綠色: 2次overdraw
  • 粉色: 3次overdraw
  • 紅色: 4次及4次以上的overdraw
    (一般來說, 藍色是可接受的, 是性能優的)

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

繪製了多重背景.
繪製了不可見的UI元素.
打開應用, 展示是這樣的:

這裏寫圖片描述

可以看到是中間列表這塊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

刪除外層ViewPager的背景再看:

這裏寫圖片描述

可以發現中間列表區域已經不再是紅色了, 但是也沒有達到藍色這個可以接受的層級. 這是因爲我們的Activity默認情況下, theme會給window設置一個純色的背景. 因爲我們這裏不想使用這個默認的背景,故而給layout加了一層背景, 導致了多重繪製背景.
(當然我們也可以自定義主題, 將theme的window background設置成我們想要的, 而不在佈局中設置.)

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

設置主題:

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

或是代碼設置, 在onCreate中:

getWindow().setBackgroundDrawable(null);

此時我們看到的效果:

這裏寫圖片描述

已基本達到優化水平.

2.3 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();
 }

2.4 頻繁的GC

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

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

導致頻繁GC有兩個原因:

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

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

這裏寫圖片描述

一般來說瞬間大量產生對象一般是因爲我們在代碼的循環中new對象, 或是在onDraw中創建對象,或者是在ListView的getView中創建對象等. 所以說這些地方是我們尤其需要注意的…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章