Android性能優(yōu)化之CPU Profiler

低性能的APP常見的表現(xiàn)有啟動/界面切換慢、動畫掉幀、卡頓、耗電,甚至出現(xiàn)應(yīng)用無響應(yīng)、程序崩潰的現(xiàn)象。當(dāng)我們著手解決這些性能問題時,面對的第一個問題就是需要找到合適的工具來檢測這些問題,用肉眼觀察來判斷定位這類問題是不靠譜的。理想的檢測工具要能做到兩點:

  1. 一是可以定性的告訴我們應(yīng)用是否有低性能問題,并且能定位到的點,指出哪個邏輯哪個方法使用系統(tǒng)資源低效,以便我們針對具體的問題給出對應(yīng)的優(yōu)化方案;
  2. 二是能定量說明問題點的嚴重程度,有具體的數(shù)字來衡量,這樣我們對比優(yōu)化前后的測量數(shù)據(jù),就可以清晰地看到優(yōu)化方案的收益和效果。
Android Profiler

為了滿足上面兩個需求,我們需要使用到systraceCPU Profiler這兩個檢測工具。

工具一:systrace
這個工具的作用從名字‘system trace’系統(tǒng)跟蹤就能看出一二。我們主要是通過這個工具來記錄分析系統(tǒng)級函數(shù)的執(zhí)行情況。它從android內(nèi)核收集CPU調(diào)度、存儲器訪問、應(yīng)用線程等信息,生成一張html格式的報表。這個工具還會自動分析這些數(shù)據(jù),高亮警示開發(fā)者注意這些可能有問題的地方。比如下圖UI掉幀的問題。但是systrace只提供了系統(tǒng)方法調(diào)用信息,沒有指明具體是因為調(diào)用我們APP程序哪個方法導(dǎo)致的,從systrace中只能定位出大概是在首個Activity創(chuàng)建的時候發(fā)生的。想要獲取關(guān)于APP執(zhí)行的詳細信息就得使用今天的主角CPU Profiler了。

systrace

點擊查看systrace更多詳情。

工具二:CPU Profiler
CPU Profiler 可實時檢查應(yīng)用的 CPU 使用率和線程運行情況,并記錄函數(shù)跟蹤,以便我們優(yōu)化和調(diào)試相關(guān)代碼。簡單點說就是我們可以通過CPU Profiler查看應(yīng)用在某段時間里某個線程執(zhí)行了哪些方法,并且還定量的展示了執(zhí)行這些方法所耗費的時間及其方法的調(diào)用堆棧。稍有經(jīng)驗的程序員都知道應(yīng)用啟動慢、界面切換慢、動畫不流暢卡頓等類似問題基本都是UI刷新不及時的表現(xiàn),UI刷新不及時就是因為UI線程被其他邏輯方法長時間占用導(dǎo)致。吶,CPU Profiler簡直就是為了解決這個問題而生的,輕松的分析出在卡頓(或者其他)過程中主線程都執(zhí)行了哪些耗時操作。

Google官方提供的Android開發(fā)工具Android Studio附帶了很多開發(fā)調(diào)試工具,這其中就包括了一系列的性能分析工具。在Android Studio 3.0之前這些工具叫Android Monitor tools,Android Studio 3.0開始成為Android Profiler tools。我現(xiàn)在使用的Android Studio版本是3.2.1,這篇文章主要介紹的是3.2.1版本Android Profiler tools中的CPU Profiler - CPU分析器。下面我們來看下CPU Profiler的使用方法。

一、CPU Profiler概覽

1、打開CPU Profile界面
  1. 點擊 View > Tool Windows > Android Profiler(也可以點擊工具欄中的 Android Profiler )。
  2. 在 Android Profiler 工具欄中點擊“+”號選擇您想要分析的設(shè)備和應(yīng)用進程。 如果您通過 USB 連接了某個設(shè)備但該設(shè)備未在設(shè)備列表中列出,請確保您已啟用 USB 調(diào)試。
  3. 點擊 CPU 時間線中的任意位置即可打開 CPU Profiler。Android Profiler 界面是CPU、內(nèi)存、網(wǎng)絡(luò)、電量4項性能數(shù)據(jù)共享時間線視圖,此共享時間線視圖只顯示時間線圖表。 要詳細分析,需要點擊性能數(shù)據(jù)對應(yīng)的圖表,打開詳情視圖。
Android Profiler 共享時間線視圖

打開 CPU Profiler 后,可以看到類似下圖的一些內(nèi)容。它將立即開始顯示應(yīng)用的 CPU 使用率和線程 Activity。

CPU Profiler
  1. Event 時間線: 顯示應(yīng)用中在其生命周期不同狀態(tài)間轉(zhuǎn)換的 Activity,并表明用戶與設(shè)備的交互,包括觸摸事件、按鍵點擊的事件。 如需了解有關(guān) Event 時間線的更多信息,包括如何啟用它,請閱讀 啟用高級分析。

  2. CPU 時間線: 顯示應(yīng)用的實時 CPU 使用率(以占總可用 CPU 時間的百分比表示)以及應(yīng)用使用的總線程數(shù)。 此時間線還顯示其他進程的 CPU 使用率(如系統(tǒng)進程或其他應(yīng)用),以便您可以將其與您的應(yīng)用使用率進行對比。 通過沿時間線的水平軸移動鼠標,您還可以檢查歷史 CPU 使用率數(shù)據(jù)。

  3. 線程活動時間線: 列出屬于應(yīng)用進程的每個線程并使用下面列出的顏色沿時間線標示它們的狀態(tài)。 在記錄一個函數(shù)跟蹤后,可以從此時間線中選擇一個線程以在跟蹤窗格中檢查其數(shù)據(jù)。

    • 綠色區(qū)域: 表示線程處于活動狀態(tài)或準備使用 CPU。 即,它正在“運行中”或處于“可運行”狀態(tài)。
    • 黃色區(qū)域: 表示線程處于活動狀態(tài),但它正在等待一個 I/O 操作(如磁盤或網(wǎng)絡(luò) I/O),然后才能完成它的工作。
    • 灰色區(qū)域: 表示線程正在休眠且沒有消耗任何 CPU 時間。 當(dāng)線程需要訪問尚不可用的資源時偶爾會發(fā)生這種情況。 線程進入自主休眠或內(nèi)核將此線程置于休眠狀態(tài),直到所需的資源可用。
  4. Record按鈕:現(xiàn)在當(dāng)我們與應(yīng)用交互時,可以通過 CPU Profiler 監(jiān)控 CPU 使用率和線程狀態(tài)了。 不過,如想要了解應(yīng)用執(zhí)行代碼的詳細信息, 我們需要記錄和檢查函數(shù)跟蹤。

2、記錄和檢查函數(shù)跟蹤

要開始記錄函數(shù)跟蹤,從下拉菜單中選擇 SampledInstrumented 記錄配置,或選擇您創(chuàng)建的自定義記錄配置,然后點擊 Record 。 與應(yīng)用交互并在完成后點擊 Stop recording。 分析器將自動選擇記錄的時間范圍,并默認在函數(shù)跟蹤窗格中顯示主線程函數(shù)跟蹤信息,如下圖所示。如果您想檢查另一個線程的函數(shù)跟蹤,只需從線程活動時間線中選中它,函數(shù)跟蹤窗格就會切換成所選線程的函數(shù)跟蹤信息。

跟蹤函數(shù)

  1. 選擇時間范圍: 用于確定您要在跟蹤窗格中檢查所記錄時間范圍的哪一部分。 當(dāng)您首次記錄函數(shù)跟蹤時,CPU Profiler 將在 CPU 時間線中自動選擇您的記錄的完整長度。 如果您想僅檢查所記錄時間范圍一小部分的函數(shù)跟蹤數(shù)據(jù),您可以點擊并拖動突出顯示的區(qū)域邊緣以修改其長度。
  2. 時間戳: 用于表示所記錄函數(shù)跟蹤的開始和結(jié)束時間(相對于分析器從設(shè)備開始收集 CPU 使用率信息的時間)。 在選擇時間范圍時,您可以點擊時間戳以自動選擇完整記錄,如果您有多個要進行切換的記錄,則此做法尤其有用。
  3. 跟蹤窗格: 用于顯示您所選的時間范圍和線程的函數(shù)跟蹤數(shù)據(jù)。 僅在您至少記錄一個函數(shù)跟蹤后此窗格才會顯示。 在此窗格中,您可以選擇想如何查看每個堆疊追蹤(使用跟蹤標簽),以及如何測量執(zhí)行時間(使用時間引用下拉菜單)。
  4. 選擇后,可通過 Top Down 樹、Bottom Up 樹、調(diào)用圖表或火焰圖的形式顯示您的函數(shù)跟蹤。 您可以在下文中了解每個跟蹤窗格標簽的更多信息。
  5. 從下拉菜單中選擇以下選項之一,以確定如何測量每個函數(shù)調(diào)用的時間信息:
    • Wall clock time:壁鐘時間表示實際經(jīng)過的時間。
    • Thread time:線程時間 = 實際經(jīng)過的時間 - 線程未消耗 CPU 資源的時間。 對于任何給定函數(shù),其線程時間始終少于或等于其壁鐘時間。 線程時間可以更好地了解到線程的實際 CPU 使用率中有多少是給定函數(shù)消耗的。

二、CPU Profiler 使用示例

上面把 CPU Profiler 工具的基本界面介紹了一下。下面我們通過兩個例子來進一步了解下 CPU Profiler 的用法。

示例1:界面切換卡頓

問題:首次打開播放頁時界面卡頓,待優(yōu)化。
分析:界面卡頓,應(yīng)該數(shù)主線有耗時操作導(dǎo)致UI刷新不及時。所以我們是用CPU Profiler來定位問題。

  1. AS(Android Studio)連接上手機,手機上運行要分析APP來到MainActivity,再在AS的Profiler窗口選擇要調(diào)試的進程,打開CPU Profiler。
  2. 然后在AS上點擊 Record ,再在手機上操作跳轉(zhuǎn)到PlayerActivity完成后點擊 Stop recording。得到如下圖所示信息。
  3. 仔細查看主線程Call Chart信息,發(fā)現(xiàn)播放頁的onCreate()里的MultiscreenManager.init();耗用了絕大多數(shù)時間。仔細查看這一方法作用是初始化投屏相關(guān)功能。(初始話方法的具體邏輯去掉了,調(diào)用了sleep方法來模擬)

Call Chart 標簽提供函數(shù)跟蹤的圖形表示形式,其中,水平軸表示函數(shù)耗費的時間,垂直軸顯示其被調(diào)用者。 對系統(tǒng) API 的函數(shù)調(diào)用顯示為橙色,對應(yīng)用自有函數(shù)的調(diào)用顯示為綠色,對第三方 API(包括 Java 語言 API)的函數(shù)調(diào)用顯示為藍色。

播放頁啟動函數(shù)調(diào)用圖

解決方案:問題找到了解決就很容易了,投屏功能不是播放頁啟動的必須初始化項目,但是如果后置到用戶點擊投屏功能以后再去初始化投屏SDK,則會影響投屏功能的用戶體驗。這種問題有一個國際標準解決方案,把該任務(wù)添加到閑時任務(wù)系統(tǒng)中去。

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_player);

    Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
        @Override
        public boolean queueIdle() {
            MultiscreenManager.init();
            return false;
        }
    });
}

CPU Profiler 檢測時測量的函數(shù)耗時會大于真正線上包運行的時間。一方面是函數(shù)跟蹤工具會加入額外的計時邏輯,另一方面它還會關(guān)閉虛擬機的JIT功能。所以我們一般是對比優(yōu)化前后測量的兩組數(shù)據(jù)來判斷優(yōu)化的效果。

修改后再測試下數(shù)據(jù),onCreate()方法耗時明顯降低,投屏sdk初始化邏輯放在主線程空閑的時候去執(zhí)行的。還有一點注意下,可能出現(xiàn)從播放頁打開到用戶請求投屏?xí)r,主線程一直不空閑,也就是我們的初始化任務(wù)沒有執(zhí)行情況。所以加到閑時任務(wù)系統(tǒng)中的任務(wù)還得具備一個特點,就是如果顯示任務(wù)沒有執(zhí)行,后續(xù)邏輯也要能正常運行。不過這個問題也很容易解決,就是在后續(xù)邏輯執(zhí)行前先判斷下是否初始化了。

優(yōu)化后數(shù)據(jù)

示例2:應(yīng)用冷啟動慢

問題:殺掉進程后,再次啟動應(yīng)用慢,待優(yōu)化。
分析:有了上面的經(jīng)驗,分析這個問題也一樣,先打開APP,選擇要調(diào)試的進程,點擊record按鈕跟蹤卡頓過程中函數(shù)調(diào)用信息。好了,問題來了,我們這里是要抓取應(yīng)用冷啟動過程函數(shù)調(diào)用信息,但是要用CPU Profiler工具抓取信息得先指定進程。應(yīng)用啟動前又沒有進程信息可以指定。愁,撓頭,程序員的頭就是這么給撓禿頂?shù)摹?/p>

這個時候就該Debug 類出場了。我們可使用 Debug 類精確地控制設(shè)備何時開始和停止記錄函數(shù)跟蹤信息,來生成一份函數(shù)跟蹤信息文件。然后再使用 Android Studio 或 Traceview 查看各個跟蹤日志。

Traceview 有點過時了。如果我們使用的是3.2及其更新的Android Studio,就沒有必要用Traceview了。

在開始生成跟蹤日志之前,要確保應(yīng)用有權(quán)限寫入外部存儲WRITE_EXTERNAL_STORAGE,以便將跟蹤日志保存至該設(shè)備。創(chuàng)建跟蹤日志,在想系統(tǒng)開始記錄跟蹤數(shù)據(jù)的位置調(diào)用 Debug.startMethodTracing(),要停止跟蹤的位置請調(diào)用 Debug.stopMethodTracing()。系統(tǒng)將在getExternalFilesDir() 目錄下生成 .trace 文件,一般都在 ~/sdcard/Android/data/$packname/files 目錄中。用Android Studio的Device File Explorer工具找到這個文件雙擊即可打開。

public class DymApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        
        SimpleDateFormat date = new SimpleDateFormat("dd_MM_yyyy_hh_mm_ss");
        String logDate = date.format(new Date());
        
        // 在Application創(chuàng)建的時候開始函數(shù)跟蹤
        // 傳入的參數(shù)是函數(shù)跟蹤信息文件名,加時間戳保證文件不會被覆蓋
        Debug.startMethodTracing("sample-" + logDate);
    }
}

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);

        try {
            // TODO: 2018/12/7 怎么又是這個倒霉的sleep,為了測試APP啟動卡頓問題
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 在首頁Activity創(chuàng)建完成停止函數(shù)跟蹤
        Debug.stopMethodTracing();
    }
}

函數(shù)跟蹤日志生成了,分析就和之前沒什么兩樣了。

CPU Profiler 中還有一些信息也是很有用的,比如Top Down 和 Bottom Up ,這里沒有講到,大家可以自己去看下,有什么不明白的,也可以在下面留言討論。

三、結(jié)束

總的來說就是 CPU Profiler 可以讓我們查看應(yīng)用進程中的每個線程,某段時間內(nèi)執(zhí)行了哪些函數(shù),以及在其執(zhí)行期間每個函數(shù)消耗的 CPU 資源(文章中耗時只得就是占用CPU的時間)。 還可以使用函數(shù)跟蹤來識別調(diào)用方被調(diào)用方。 據(jù)此可以確定哪些函數(shù)負責(zé)調(diào)用常常會消耗大量特定資源的任務(wù),并嘗試優(yōu)化應(yīng)用代碼以避免不必要的開支。

大家努力,最大限度減少應(yīng)用的 CPU 使用率,向德芙巧克力一樣,在各種新舊設(shè)備上都能提供縱享絲滑的用戶體驗。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容