索引
Android中的App啟動,是從用戶點擊應用圖標的那一刻起,到應用界面顯示出來的過程。這段過程有的應用耗時少工秩,有的應用則耗時多,如果我們想要對那些耗時多的應用進行優(yōu)化。那么助币,首先我們需要知道從應用圖標被點擊浪听,到界面加載出來發(fā)生了什么。然后眉菱,我們需要知道這段過程中為什么會發(fā)生部分耗時長的操作迹栓,最后,我們需要知道如何優(yōu)化一款App的啟動速度
目錄
- 應用圖標被點擊到界面加載出來的過程
- 啟動中出現(xiàn)的問題及其原因
- 常用的啟動優(yōu)化手段
1. App的啟動過程
1.1 App的點擊時的啟動邏輯
首先俭缓,我們要知道Android系統(tǒng)的桌面就是一個應用迈螟,我們的應用圖標在桌面上的那個界面其實就是一個Activity,當我們的應用圖標被點擊時尔崔,就觸發(fā)了這個Activity的點擊事件
位于com.android.launcher2
包下
public final class Launcher extends Activity
當點擊Activity界面上的一個item(也就是一款應用的icon)的時候答毫,會調用它的onClick方法
public void onClick(View v) {
if (v.getWindowToken() == null) {
return;
}
if (!mWorkspace.isFinishedSwitchingState()) {
return;
}
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
...
//這里是啟動Activity
boolean success = startActivitySafely(v, intent, tag);
if (success && v instanceof BubbleTextView) {
mWaitingForResume = (BubbleTextView) v;
mWaitingForResume.setStayPressed(true);
}
} ...
}
上面代碼較多,所以省去其他的邏輯季春,只需要看到其中一行調用startActivitySafely洗搂,就是啟動Activity
boolean startActivitySafely(View v, Intent intent, Object tag) {
boolean success = false;
try {
success = startActivity(v, intent, tag);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
}
return success;
}
//從startActivitySafely調用而來的
boolean startActivity(View v, Intent intent, Object tag) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
boolean useLaunchAnimation = (v != null) &&
!intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
UserHandle user = (UserHandle) intent.getParcelableExtra(ApplicationInfo.EXTRA_PROFILE);
LauncherApps launcherApps = (LauncherApps)
this.getSystemService(Context.LAUNCHER_APPS_SERVICE);
if (useLaunchAnimation) {
...
} else {
if (user == null || user.equals(android.os.Process.myUserHandle())) {
//這里是真正啟動Activity的代碼
startActivity(intent);
} else {
launcherApps.startMainActivity(intent.getComponent(), user,
intent.getSourceBounds(), null);
}
}
return true;
} catch (SecurityException e) {
...
}
return false;
}
從startActivitySafely到startActivity,最終會調用到Activity中的startActivity(intent)方法载弄,這個方法就和平時啟動Activity是一樣的
1.2 startActivity的調用步驟
- 這時的startActivity會開啟一個新的app
- 首先會加載一個main函數(shù)耘拇,并分配一些內(nèi)存區(qū)域(包括方法區(qū),堆區(qū)和java棧)
- java棧中會調用到ActivityThread中的main函數(shù)宇攻,實例化一個Application
- 調用Application的onCreate方法
- 開啟MainActivity
2 啟動中出現(xiàn)的問題及其原因
2.1 啟動白屏
白屏:是由于AppCompat的主題屬性:windowBackgroud導致的惫叛,他是一個白色的背景
3 啟動優(yōu)化手段
3.1 啟動速度
通過Display
這個關鍵字,就可以在log里獲取到系統(tǒng)打印的啟動時間
3.1.1 啟動速度測試
adb shell am start -W 包名/全類名
參考對比:QQ
adb shell am start -W com.tencent.mobileqq/com.tencent.mobileqq.activity.SplashActivity
啟動時就會有時間
調用這句命令時逞刷,會走到這個方法
//AM.java
result = mAm.startActivityAndWait(null, null, intent, mimeType,
null, null, 0, mStartFlags, profilerInfo, null, mUserId);
//返回的result里面嘉涌,就有時間信息
3.2.1 啟動速度相關時間
WaitTime
startTime記錄的剛準備調用startActivityAndWait()的時間點
endTime記錄的是startActivityAndWait()函數(shù)調用返回的時間點
WaitTime = startActivityAndWait()調用耗時。
ThisTime和TotalTime
解釋下代碼里curTime夸浅、displayStartTime仑最、mLaunchStartTime三個時間變量.
curTime表示該函數(shù)調用的時間點.
displayStartTime表示一連串啟動Activity中的最后一個Activity的啟動時間點.
mLaunchStartTime表示一連串啟動Activity中第一個Activity的啟動時間點
正常情況下點擊桌面圖標只啟動一個有界面的 Activity,此時 displayStartTime 與mLaunchStartTime 便指向同一時間點帆喇,此時 ThisTime=TotalTime警医。另一種情況是點擊桌面圖標應用會先啟動一個無界面的 Activity 做邏輯處理,接著又啟動一個有界面的Activity坯钦,在這種啟動一連串 Activity 的情況下(知乎的啟動就是屬于這種情況)预皇,displayStartTime 便指向最后一個 Activity 的開始啟動時間點,mLaunchStartTime 指向第一個無界面Activity的開始啟動時間點婉刀,此時 ThisTime吟温!=TotalTime。
一個應用 Activity pause 的耗時路星。也就是說溯街,開發(fā)者一般只要關心 TotalTime 即可诱桂,這個時間才是自己應用真正啟動的耗時洋丐。
最后總結一下呈昔,如果只關心某個應用自身啟動耗時,參考TotalTime友绝;如果關心系統(tǒng)啟動應用耗時堤尾,參考WaitTime;如果關心應用有界面Activity啟動耗時迁客,參考ThisTime
ThisTime郭宝、TotalTime 的計算在 frameworks\base\services\core\java\com\android\server\am\ActivityRecord.java 文件的 reportLaunchTimeLocked() 函數(shù)中
3.2 主題優(yōu)化(簡單)
給splash界面設置主題。例如app啟動時啟動的是AActivity掷漱,那么就給AActivity設置一個單獨的主題Launch
然后在AActivity的onCreate方法中粘室,在super.onCreate之前,將主題還原
<!--APP的基礎主題-->
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
<!--APP的啟動時使用的主題-->
<style name="LaunchTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="android:windowBackground">@drawable/bg</item>
</style>
然后在第一個顯示的activity中卜范,將主題替換回來
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState)
Thread.sleep(2000)//do something
setTheme(R.style.AppTheme);
setContentView(R.layout.activity_main)
}
這樣使用衔统,就可以避免app在啟動時,耗時過長導致的白屏問題海雪。當然锦爵,也可以不使用特定的背景,而使用透明背景奥裸,也是可行的险掀。例如
<!--APP的啟動時使用的主題-->
<style name="LaunchTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="android:windowIsTranslucent">true</item>
</style>
3.3 trace文件優(yōu)化
File file = new File(Environment.getExternalStorageDirectory(), "app7");
Debug.startMethodTracing(file.getAbsolutePath());
//執(zhí)行方法
Debug.stopMethodTracing()
通過startMethodTracing方法可以獲取trace文件,來分析方法執(zhí)行的時機和次數(shù)
對于那些耗時較長的方法湾宙,可以通過以下幾種方式優(yōu)化
1樟氢、子線程
2、懶加載
對于那些耗時特別長的方法侠鳄,可以具體分析嗡害,優(yōu)化代碼
總結
到此,App啟動也基本總結完了畦攘,最好還是那一句霸妹,良好的代碼習慣才是最重要的
注:此文屬作者原創(chuàng),轉載請標明出處