【Android 性能优化】应用启动优化 ( 启动白屏问题 | 应用启动时间测量 | 冷启动 | 热启动 | 应用启动时间计算源码分析 )
文章目錄
- 一、 APP 啟動白屏 / 黑屏
- 二、 APP 啟動速度測量
- 1. 通過 Logcat 日志查看應用啟動時間
- 2. 通過 adb 命令查看界面啟動時間
- 三、 APP 冷啟動與熱啟動
- 四、 APP 啟動時間計算
一、 APP 啟動白屏 / 黑屏
1 . 問題描述 : Android 應用啟動時 , 尤其是大型復雜的應用 , 經常出現幾秒鐘的白屏或黑屏 , 黑屏或白屏取決于主界面 Activity 的主題風格 ;
2 . 解決方案 : Android 應用啟動時很多大型應用都會有一個幾秒的倒計時 , 并配上動態廣告 , 這并不是開發者想要放上去的 , 而是為了避免上述啟動白屏導致用戶體很差 ;
3 . 啟動優化 : 這里的應用啟動白屏 / 黑屏的時間 , 是可以通過優化解決的 , 如將 333 秒鐘的啟動白屏卡頓 , 優化為毫秒級 ;
二、 APP 啟動速度測量
1. 通過 Logcat 日志查看應用啟動時間
通過 Logcat 日志查看應用啟動時間 : 該方法只適用于 Android 4.4 版本之后的手機 ;
① 使用工具 : 使用 Android Studio 中的 Logcat 日志查看工具 ;
② 過濾選項 : 設置過濾選項為 No Filters , 這是系統 ActivityTaskManager 打印的 , 不屬于應用日志信息 ;
③ 關鍵字 : 使用 Display 關鍵字過濾 ;
④ 截取到的日志信息如下 :
2020-06-20 08:44:09.821 1266-1305/? I/ActivityTaskManager: Displayed kim.hsl.recyclerview/.MainActivity: +334ms 2020-06-20 08:44:18.457 1266-1305/? I/ActivityTaskManager: Displayed com.google.android.permissioncontroller/com.android.packageinstaller.permission.ui.GrantPermissionsActivity: +315ms 2020-06-20 08:44:22.308 1266-1305/? I/ActivityTaskManager: Displayed kim.hsl.cckfa/.MainActivity: +311ms⑤ 具體操作選項參考下面的截圖 :
2. 通過 adb 命令查看界面啟動時間
通過 adb 命令查看界面啟動時間 :
① 命令格式 : adb shell am start -W 包名/完整 Activity 類名 ;
② 本次命令 : adb shell am start -W kim.hsl.rtmp/kim.hsl.rtmp.MainActivity , 其中 kim.hsl.rtmp 是包名 , kim.hsl.rtmp.MainActivity 是完整的類名 ;
Microsoft Windows [版本 10.0.18362.900] (c) 2019 Microsoft Corporation。保留所有權利。C:\Users\octop>adb shell am start -W kim.hsl.rtmp/kim.hsl.rtmp.MainActivity Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=kim.hsl.rtmp/.MainActivity } Status: ok LaunchState: COLD Activity: kim.hsl.rtmp/.MainActivity TotalTime: 374 WaitTime: 376 CompleteC:\Users\octop>③ 命名截圖 : 下面的截圖作為參考 ;
④ Activity 界面源碼 : 上述啟動類類對應 RTMP_Pusher 應用中的主界面 ;
package kim.hsl.rtmp; public class MainActivity extends AppCompatActivity {//... }三、 APP 冷啟動與熱啟動
熱啟動的速度要遠高于冷啟動 ;
1 . 冷啟動 : 應用后臺沒有該應用 , 該應用所有資源都要重新加載 , 分配新的進程 , 初始化 Application , 初始化 Activity 界面 ; 下圖中的 LanuchState 中顯示了啟動的模式 , 下圖中顯示的是冷啟動 ;
2 . 熱啟動 : 按下 Home 鍵 , 應用進入后臺 , 再次啟動 , 應用由后臺轉到前臺 , 這種啟動稱為熱啟動 ; 下圖中的 LanuchState 中顯示了啟動的模式 , 下圖中顯示的是熱啟動 ;
四、 APP 啟動時間計算
1 . 啟動 Activity 命令 : adb shell am start -W 包名/完整 Activity 類名 ;
2 . 日志打印內容 :
C:\Users\octop>adb shell am start -W kim.hsl.rtmp/kim.hsl.rtmp.MainActivity Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=kim.hsl.rtmp/.MainActivity } Status: ok LaunchState: COLD Activity: kim.hsl.rtmp/.MainActivity TotalTime: 431 WaitTime: 433 CompleteC:\Users\octop>3 . am 命令及源碼 : 在上述命令中可以啟動 Android 應用 Activity 界面 , 其中 am 命令是由 Am.java 程序編譯出來的 , 其路徑是 \frameworks\base\cmds\am\src\com\android\commands\am\Am.java ;
4 . 分析 Am.java 可執行文件 :
① 執行啟動 Activity 相關邏輯 : 啟動前后都記錄了時間 , 整個啟動過程就是通過這兩個時間計算出來的 ;
IActivityManager.WaitResult result = null; int res;// 記錄開始時間 final long startTime = SystemClock.uptimeMillis(); if (mWaitOption) {// 啟動 Activity result = mAm.startActivityAndWait(null, null, intent, mimeType,null, null, 0, mStartFlags, profilerInfo, null, mUserId);res = result.result; } else {res = mAm.startActivityAsUser(null, null, intent, mimeType,null, null, 0, mStartFlags, profilerInfo, null, mUserId); } // 記錄結束時間 final long endTime = SystemClock.uptimeMillis();② 打印啟動時間相關源碼 : IActivityManager.WaitResult 對象中封裝了 thisTime , totalTime 等時間信息 ; 這些時間都是在啟動過程中計算的 ;
if (mWaitOption && launched) {if (result == null) {result = new IActivityManager.WaitResult();result.who = intent.getComponent();}System.out.println("Status: " + (result.timeout ? "timeout" : "ok"));if (result.who != null) {System.out.println("Activity: " + result.who.flattenToShortString());}if (result.thisTime >= 0) {System.out.println("ThisTime: " + result.thisTime);}if (result.totalTime >= 0) {System.out.println("TotalTime: " + result.totalTime);}System.out.println("WaitTime: " + (endTime-startTime));System.out.println("Complete"); }5 . 分析啟動時間計算源碼 :
① 計算啟動時間源碼 : APP 啟動時間計算在 \frameworks\base\services\core\java\com\android\server\am\ActivityRecord.java 類中計算 ;
② 在 windowsDrawn 方法中調用 reportLaunchTimeLocked 計算時間 :
public void windowsDrawn() {synchronized(service) {if (displayStartTime != 0) {// 該方法是入口方法reportLaunchTimeLocked(SystemClock.uptimeMillis());}mStackSupervisor.sendWaitingVisibleReportLocked(this);startTime = 0;finishLaunchTickingLocked();if (task != null) {task.hasBeenVisible = true;}} }③ 啟動時間計算 : 可以看到在 reportLaunchTimeLocked 方法中 , 計算了 thisTime 和 totalTime 的時間值 ;
private void reportLaunchTimeLocked(final long curTime) {final ActivityStack stack = task.stack;// 這里計算了 APP 啟動時間final long thisTime = curTime - displayStartTime;final long totalTime = stack.mLaunchStartTime != 0? (curTime - stack.mLaunchStartTime) : thisTime;if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) {Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching", 0);EventLog.writeEvent(EventLogTags.AM_ACTIVITY_LAUNCH_TIME,userId, System.identityHashCode(this), shortComponentName,thisTime, totalTime);StringBuilder sb = service.mStringBuilder;sb.setLength(0);sb.append("Displayed ");sb.append(shortComponentName);sb.append(": ");TimeUtils.formatDuration(thisTime, sb);if (thisTime != totalTime) {sb.append(" (total ");TimeUtils.formatDuration(totalTime, sb);sb.append(")");}Log.i(ActivityManagerService.TAG, sb.toString());}mStackSupervisor.reportActivityLaunchedLocked(false, this, thisTime, totalTime);if (totalTime > 0) {//service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime);}displayStartTime = 0;stack.mLaunchStartTime = 0;}6 . APP 啟動時間總結 :
① 開始時間 : 點擊 APP 圖標 , 該時間就是 Am.java 中記錄的 startTime 開始時間 ;
// 記錄開始時間 final long startTime = SystemClock.uptimeMillis();② 系統分配內存時間 : 之后 Android 系統會給 APP 分配內存 , 這段時間是無法進行優化的 , 用戶無法控制 , 占用時間由系統控制 ;
③ 畫面繪制完畢調用方法 : 當調用 ActivityRecord.java 中的 windowsDrawn 方法時 , 畫面繪制完畢 , 此時會調用 reportLaunchTimeLocked 方法 , 傳入當前時間 SystemClock.uptimeMillis() 作為參數 ;
if (displayStartTime != 0) {// 該方法是入口方法reportLaunchTimeLocked(SystemClock.uptimeMillis());}④ 畫面繪制時間 : 在 reportLaunchTimeLocked 方法中 , 計算 thisTime 時間 , curTime 是傳入的 SystemClock.uptimeMillis() 參數 , 即當前時間 , displayStartTime 是畫面開始繪制的時間 , thisTime 就是畫面繪制時間 ;
final ActivityStack stack = task.stack; final long thisTime = curTime - displayStartTime;⑤ 總時間計算 : 這里計算總時間時 , 需要根據當前是冷啟動還是熱啟動進行不同的計算 , 如果是冷啟動 , (curTime - stack.mLaunchStartTime) 時間是總時間 , thisTime 是熱啟動的啟動總時間 ;
final long totalTime = stack.mLaunchStartTime != 0? (curTime - stack.mLaunchStartTime) : thisTime; 《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的【Android 性能优化】应用启动优化 ( 启动白屏问题 | 应用启动时间测量 | 冷启动 | 热启动 | 应用启动时间计算源码分析 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android RTMP】安卓直播推流
- 下一篇: 【Android 性能优化】应用启动优化