App卡頓-從零瞭解到系統解決 一、 卡頓有哪些場景 二、卡頓定義 三、最常見卡頓的解決方案 五 思考題目

一、 卡頓有哪些場景

    首先,回想下在什麼情況下你會覺得某個App很卡,不妨設想現在從手機桌面打開一個App-簡書。
    1 當App啓動後很久才進入主頁面,你會覺得卡;
    2 當主頁面內容很久才展示完全,你會覺得卡;
    3 當在列表頁面滑動時出現停頓,你會覺得卡;
    4 當點擊某篇文章停留很久才跳轉,你會覺得卡;
    5 當點擊訂閱按鈕,很久纔有訂閱成功反饋,你會覺得卡;

總結提煉下,你會發現卡頓這種視覺感知問題歸根到底是事件處理和UI展示的綜合消耗時間超出了用戶感官系統的期待時間。精確一點就可給出如下定義:

二、卡頓定義

    在能夠感知的視覺場景中,當事件處理(思考)和UI展示(表達)的綜合消耗時間超過用戶視覺系統的最大期待時間,我們就說出現了卡頓。
    在卡頓描述中有提到一個概念用戶視覺系統的期待時間,這個期待時間是主觀的,但要小於大多數用戶的期待時間,它在一定條件下又是客觀的。比如當點擊訂閱按鈕,App會彈出訂閱成功或者訂閱失敗彈框,有人等待1s也沒覺得有問題,也有人超過0.4s就感覺體驗很不爽,但我們開發者要關注的是所有用戶的期待時間,所以閾值一定是讓大多數用戶感覺爽的,若點擊App中任意按鈕、視圖,都在0.1s內給出反饋,這樣基本上99%的人都是感覺-哇哦~你們App反應好快。
    除了上述事件反饋時間,還有一個我們人類視覺系統硬件帶來的期待時間,那就是連續動畫中單個畫面的渲染時間。連續的動畫中一個畫面的如果在視覺暫留時間內沒有渲染好,顯示系統將會展示上一幀頁面,那麼對於用戶來說就是發生了卡頓。通常用fps衡量渲染速度,fps(frame per second)是一秒鐘系統的渲染頁面的總次數。渲染速度越快,平均一幀渲染時間就越短,我們就感覺越絲滑。但由於人類的視覺暫留時間基本都大於16.6ms,所以我們單幀渲染時間小於16.6ms(也就是幀率60fps以上)就可以使大多數人感覺非常流暢。

    通過卡頓的定義,我們找到了解決卡頓的關鍵兩要素:
    1 事件數據處理時間
    2 UI渲染時間
    我們要做的是在用戶能夠感知的使用場景中,給出優化事件處理時間和UI渲染時間的方案。
    結合用戶感知最多的卡頓場景,可以得出一個較全面的卡頓解決方案:

三、最常見卡頓的解決方案

3.1 App冷啓動優化

冷啓動具體時間段界定

startTime:用戶點擊桌面圖標開始
點擊桌面圖標->點擊事件回調到桌面(Launcher) App->Launcher處理點擊事件,收集該圖標相關的信息,發起intent調用->跨進程調用AMS啓動對應的進程
然後再ActivityStarter打印log:

2021-12-16 14:18:50.402 24772-24772/com.example.demo V/JG: launcher onClick start
2021-12-16 14:18:50.403 534-945/system_process I/ActivityTaskManager: START u0 {flg=0x10000000 cmp=com.jingang.lifechange/.SplashActivity} from uid 10164
2021-12-16 14:18:50.449 534-563/system_process I/ActivityManager: Start proc 24932:com.jingang.lifechange/u0a162 for pre-top-activity {com.jingang.lifechange/com.jingang.lifechange.SplashActivity}
//第一句log是我們模擬桌面App的點擊事件;第二句log是AMS開始啓動SplashActivity,第三句log是ams發現該Activity所在的進程未啓動去啓動進程。

從點擊桌面到這兩個log打印,過程有很多步驟,但這些步驟裏面沒有耗時操作,一般情況下非常短暫(2ms內),而且沒有log,所以一般情況下把上述第二句logSTART u0 ~*的時間當作App冷啓動的開始時間。當然一些特殊情況需要定位問題到底在哪邊需要精確定位時間,我們就要想辦法去無限接近用戶點擊桌面圖標的時間,這個需要去了解Android 輸入系統或觀察Launcher app點擊日誌(基本沒有),以後介紹。
但只有代碼運行到自己的App進程之後,我們纔能有所作爲,所以還要記錄下冷啓動時候App最早收到回調的時間點-這也是優化的起點時間:

public class MainApplication extends Application {
    private static final String TAG = "lifeCycle:"+MainApplication.class.getName();
    @Override
    protected void attachBaseContext(Context base) {
        Log.v(TAG,"attachBaseContext");
        super.attachBaseContext(base);
    }
}

endTime:第一個頁面主體內容展示出來結束
如果有splash頁面,那就是Home頁面主體展示出來結束。
home頁面主體展示出來,比較精確的就是取第一幀圖像繪製出來的時間。

    @Override
    protected void onResume() {
        super.onResume();
        Log.v(getTag(),"onResume");
        getWindow().getDecorView().getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
            @Override
            public void onDraw() {
                Log.v(getTag(),"onDecorView draw , draw time");
            }
        });
    }

當然也可以利用系統log,如下的第二句log

2021-12-16 15:19:35.979 28022-28022/com.jingang.lifechange V/lifeCycle:MainActivity: onDecorView draw , draw time 0
2021-12-16 15:19:36.010 534-561/system_process I/ActivityTaskManager: Displayed com.jingang.lifechange/.MainActivity: +1s863ms
2021-12-16 15:19:36.018 28022-28022/com.jingang.lifechange V/lifeCycle:MainActivity: onDecorView draw , draw time 1
2021-12-16 15:19:36.040 28022-28022/com.jingang.lifechange V/lifeCycle:MainActivity: onDecorView draw , draw time 2
冷啓啓動時間指標

4s以內良,8s以外差————用戶點擊後桌面圖標後, 心裏開始 數1 、2、 3 到4頁面還沒有出來,用戶開始着急,數到7、8沒出來用戶一般就放棄了。也就是上面我們記錄的結束時間減去開始時間最好不好超過4s。

冷啓動優化方案

原則: 視覺優化、異步、 懶加載、協調加載順序
實現:
1)用戶點擊桌面應用圖標——到正式展示出來假如只有4S,但4S內出現了點擊無反應、黑屏、白屏,那用戶主觀感覺也是非常糟糕。所以我們把視覺優化放到第一個要優化的項目,最常見的就是把冷啓動的Activity增加一個帶有背景(閃屏圖)的主體(Theme)。
2)把沒有必要放在UI線程中的初始化任務放入其他線程;
3)把沒有必要在應用啓動時的初始化任務移動到真正使用之前,或者利用應用線程空餘時間進行;
4)注意很多初始化任務是有順序的,在優化過程中這些順序要注意保持;
如果是一個大型項目,app啓動初始化任務特別多,有很多初始化任務之間有加載順序問題,初始化也不一定在UI線程進行,那我們可以大幹一場,構建一個App啓動器。App啓動器是一個任務調度工具類,可以把不同的初始化任務按照順序在不同線程中執行,從而使App啓動正確而高效,做好之後也可以在不同項目中複用。這個以後再探討如何設計和實現。

3.2 頁面跳轉卡頓優化

頁面跳轉時間段界定
startTime:發起頁面用戶事件
endTime:打開頁面首幀加載完成
頁面跳轉時間指標
1s內- 頁面秒開,無他頁面秒開基本稱爲一個用戶在App內操作的一個潛在標準,當然跟用戶主觀感受有關。
頁面跳轉優化概覽
普通模式下:Activity A跳轉Activity B生命週期

A onPause()->B onCreate()-> B onStart()->B onResume()->A onStop()

B頁面的首幀加載是在B onResume()回調後進行View的測量、佈局、繪製,同本文冷啓動結束的時間節點,可以參考上面進行。
由頁面跳轉的計算開始結束時間點,可得如下具體點優化點:
1)頁面跳轉發起頁面Activity A,onPause()內儘量減少UI線程耗時操作,可提升這個頁面打開其他頁面的速度;
2)頁面跳轉發起頁面Activity A,onStop()的UI線程耗時操作,雖然不會使頁面跳轉看起來加快,但因爲onStop是這個用戶操作最後一個環節,所以減少耗時操作可以減少出現ANR的概率;
3)被打開頁面Activity B 在onCreate()\onStart()\onResume()方法中儘量減少UI線程的耗時操作,提升這個頁面被打開的速度 。比如一些耗時操作移動到idleHandler中;
4)對Activity B 的View層級、佈局、繪製等進行優化也可以加快頁面B的打開速度,這個會在接下來章節繼續展開。
原則
根據不同模式下Activity啓動,涉及到的生命週期變化進行跟蹤優化。

3.3 頁面滑動,屬性動畫、幀動畫等動畫

動畫的本質是什麼?
動畫和視頻的本質都是按一定順序快速展示的一組圖片,因爲人眼的視覺暫留原理就形成了連續移動的感覺。
圖片的本質是什麼?
一張圖片的本質是一組像素點
圖片的這一組像素點如何得來
1 現成圖片:各種圖片格式,雖然可能有壓縮,但一張jpg,png等格式的圖片本質就是一組像素點。
    顯示的時候就是把這組像素點從網絡、硬盤等地方讀入到內存,由內存完成一些校驗工作,並緩存起來,等待特定時機(接收到vsync信號時)寫入到顯示器緩存區-從而顯示出來。
2 由數據生成
    操作系統都提供一套用戶定義圖像api--比如Android的顯示系統中View就是提供給用戶自定義圖像的api,用戶按照一定規範調用api描述自己想要的圖像,系統在特定時機(接收到vsync信號時)就把這種用戶規範的描述轉換成一組像素點,寫入顯示器緩存,從而顯示出來。
    我們上面談到的頁面滑動、屬性動畫都屬於第二種情況——圖片是由操作系統根據用戶的描述一步步生成,所以接下來的重點是討論這種情況如何顯示和優化。
    計算機顯示一張圖片,其實跟我們自己找人畫一張畫像然後送給朋友非常相似,都是由我們把想畫的場景告訴畫家,畫家取紙張繪製,最後我們拿到畫作,去展示給心愛的人。


    在android系統上我們稍微深入一點,得到一個更詳細的流程。

    從上述一張圖繪製流程圖可以看到,系統繪製一張圖比較耗時間的點在於1) 讀取和轉換用戶描述 ,2)繪製(包含渲染)
    除此之外,我們大多數繪製工作是要在ui線程進行,雖然android系統有消息屏障機制可保證繪製任務優先級很高,但是ui線程並不能把當前正在執行的任務終止,所以在進行繪製流程時候,遇到UI線程中有耗時很多的任務,也會導致繪製被推遲,從而造成卡頓、丟幀等。
    所以我們優化從以下兩個方面進行:
    1、佈局優化
    佈局優化就是指是採用儘量節約的方式指達到同樣的顯示效果。比如減少 View 層級,這樣會加快 View 的循環遍歷過程,比如view層級優化可以減少view;去除不必要的背景(背景是單獨繪製),可以使繪製內容減少;減少View 的過度繪製;提前把xml轉換成java代碼,減少解析時間等;
    2、 減少UI線程中耗時任務和異常阻塞
    前面有講到過UI線程會處理完當前任務,才進行繪製,如果在我們申請繪製的時候有耗時任務在執行,那勢必會影響正常顯示,android系統三緩衝機制,所以原則上一個ui線程任務超過16*3=48ms的時候就會對繪製任務造成影響,所以我們要減少這些任務。在full GC的時候也停止ui線程,影響繪製造成卡頓。

如何檢測
1 佈局優化檢測
Hierarchy Viewer、開發者模式過度繪製開關等
2 ui耗時任務檢測
通過類似下面的計算出ui線程每個任務執行的時間,找到耗時比較長的時間進行優化。

getMainLooper().setMessageLogging(new LogPrinter(Log.INFO,"uiThread"));

可以通過hook等方法,插入定位問題所需要的信息。

五 思考題目

系統有哪些監控措施是處理卡頓的?
答:ANR 、strictmode
本文分析的可全面?
答:並沒有, 我們只是找了啓動流程中最可能有問題的地方拿出來分析講解,不代表其他地方沒有問題,比如跨進程通信 、AMS狀態、當前系統cpu和內存使用狀態等。
關於流暢度未來
1 卡頓預測
基於對用戶行爲的洞察,可以預測到接下來會該顯示哪些內容,提前進行數據初始化,甚至提前走完所有繪製步驟。
2 分工與異步
對繪製過程再進行重新解讀,將當前順序執行的再次進行分工,充分利用當前多cpu和gpu架構。
3 顯示效果與功耗更加平衡
動態多線程,動態開啓gpu
4 感官優化
用戶覺得慢,這個問題是“用戶覺得慢”,不一定是真的慢。

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