Android性能優化(六)之卡頓那些事

一、 Introduction

對普通用戶而言,類如內存佔用高、耗流量、耗電量等性能問題可能不會輕易發現,可是卡頓問題用戶必定會立馬直觀的感覺到。本文就帶你一覽卡頓的發生、檢測、及優化。git

二、 The Final Reason Why It Block ?

《Android性能優化(二)之佈局優化面面觀》中咱們說到:60fps VS 16ms。github

60幀每秒是目前最合適的圖像顯示速度,也是絕大部分Android設備設置的調試頻率,若是在16ms內順利完成界面刷新操做能夠展現出流暢的畫面,而因爲任何緣由致使接收到VSYNC信號的時候沒法完成本次刷新操做,就會產生掉幀的現象,刷新幀率天然也就跟着降低(假定刷新幀率由正常的60fps降到30fps,用戶就會明顯感知到卡頓)。數據庫

卡頓感覺的由來

三、 The Ways Lead To Block

3.1 UI線程中的耗時操做

  • UI線程中有I/O讀寫、數據庫訪問等耗時操做;

3.2 複雜、不合理的佈局以及OverDraw

3.3 內存使用異常致使的卡頓

3.4 錯誤的異步方式

  • 對線程開啓方式的不一樣選擇以及不一樣配置均可能致使卡頓的發生;
    • 《Android性能優化(一)之啓動加速35%》一文中說到過:**不正確的異步任務不只不能較好的完成異步任務,反而會加重卡頓。**關於異步任務開啓的選擇,以後會出一篇詳細的文章,能夠先參考啓動加速的文章。

四、 The Ways To Find Block

4.1 StickMode

StrictMode類是Android 2.3 (API 9)引入的一個工具類,能夠用來幫助開發者發現代碼中的一些不規範的問題,以達到提高應用響應能力的目的。能夠設置不一樣的線程檢測策略、虛擬機檢測策略。性能優化

public void onCreate() {
     if (DEVELOPER_MODE) {
        // 線程檢測策略
         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                 .detectDiskReads()
                 .detectDiskWrites()
                 .detectNetwork()   // or .detectAll() for all detectable problems
                 .penaltyLog()
                 .build());
        // 虛擬機檢測策略    
         StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                 .detectLeakedSqlLiteObjects()
                 .detectLeakedClosableObjects()
                 .penaltyLog()
                 .penaltyDeath()
                 .build());
     }
     super.onCreate();
 }
複製代碼

當違規操做發生時,能夠根據自定義的策略記錄下Log或者Crash,以便於跟蹤改善。bash

4.2 TraceView

與StickMode相比,TraceView簡直是發現Block根源的神器,不只能看出每一個方法消耗的時間、發生次數,而且能夠進行排序,直接從最耗時的方法開始處優化;微信

關於TraceView的使用及分析,請參考《Android性能優化(一)之啓動加速35%》第五章節。異步

4.3 AndroidPerformanceMonitor

AndroidPerformanceMonitor 是一個檢測卡頓的開源庫,前身是BlockCanary,更前身則是LeakCanary。而其使用與LeakCanary也比較類似,能夠自主設置卡頓檢測時間,檢測到的卡頓一樣是以Notification展現,在使用體驗上也至關相似,與LeakCanary能夠說是孿生兄弟。ide

原理 利用了Looper.loop()中每一個Message被分發先後的Log打印,而咱們設置本身的Printer就能夠根據Log的不一樣的處理:工具

  • Message分發前,使用HandlerThread延時發送一個Runnable,這個時間可本身設置;
  • Message在規定的時間內完成分發,則會取消掉這個Runnable;
  • Message沒有在規定的時間內(其實是規定時間的0.8)完成分發,那這個Runnable就會被執行,能夠獲取到當前的堆棧信息;
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
    logging.println(">>>>> Dispatching to " + msg.target + " " +
            msg.callback + ": " + msg.what);
}

msg.target.dispatchMessage(msg);

if (logging != null) {
    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
複製代碼

4.4 ANR-WatchDog

ANR-WatchDog一樣是一個檢測卡頓的檢測庫,與AndroidPerformanceMonitor不同的是它的原理相對簡單:oop

  • 原理是開啓一個線程,持續循環不斷的往UI線程中Post一個Runnable(修改一個數的大小),而後在規定時間以後檢測這個Runnable是否被執行(數的大小有沒有 被修改過來)。沒有被執行的話說明主線程執行上一個Message超時,而後獲取當前堆棧信息;
  • ANR-WatchDog的原理更加簡單,可是根據使用狀況來看準確性不及AndroidPerformanceMonitor高,並且可設置的配置不如AndroidPerformanceMonitor豐富;

4.5 Choreographer

咱們知道Android系統每隔16ms都會發出VSYNC信號,觸發UI的繪製,而咱們能夠拿到回調的監聽。若是16ms沒有回調的話咱們就知道發生了卡頓。

Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
            @Override
            public void doFrame(long l) {
                
            }
});
複製代碼

備註:這種方式的原理也比較簡單,可是可用性不高,只能測出界面繪製的卡頓

五、 The Ways To Avoid Block

在第三節咱們分析了產生卡頓的緣由,那麼避免卡頓的方法就很簡單了:反其向行知便可。

5.1 將耗時操做移到異步中

  • 類如I/O讀寫、數據庫訪問等都應該採用異步的方式,不能有「只是一個很小的文件」之類的想法,防微杜漸;

5.2 合理優化佈局,避免OverDraw。

5.3 合理優化內存

  • 節省內存的分配空間,儘量的下降GC的頻率,縮短GC的平均時間;CPU不被佔用,卡頓的概率就會更低;

5.4 正確使用異步

  • 再次強調一遍:耗時操做不能都直接隨意交給異步,不正確的異步使用方式反而會加重卡頓;

六、 The Normal Ways Of Dealing Block

1. 開發中使用AndroidPerformanceMonitor檢測卡頓進行處理; 2. 任何耗時操做正確的移到異步裏; 3. 合理優化佈局,避免OverDraw; 4. 優化內存分配,減小GC頻率,這通常不是某個界面的事情,是一項長期工做;

七、 Block Check Of System

系統對Block有檢測嗎?那必須有!大名鼎鼎的ANR就來於此。

ANR的前因後果:觸發場景、分析方法,你真的都清楚嗎?歡迎關注下一篇文章,帶你細究ANR鮮爲人知的那些事。

歡迎關注微信公衆號:按期分享Java、Android乾貨!

歡迎關注
相關文章
相關標籤/搜索