谷歌官方文檔:https://developer.android.com/topic/performance/launch-time.html#common
一晃财、app啟動(dòng)方式
啟動(dòng)方式分為兩種:冷啟動(dòng)摆昧、熱啟動(dòng)攻冷、溫啟動(dòng)。
1. 冷啟動(dòng)
啟動(dòng)app時(shí)帮哈,后臺(tái)沒有app的進(jìn)程十办,或者進(jìn)程被killed,這叫冷啟動(dòng)累舷。冷啟動(dòng)因?yàn)橄到y(tǒng)會(huì)重新創(chuàng)建一個(gè)新的進(jìn)程分配給它,所以會(huì)先創(chuàng)建和初始化Application類夹孔,再創(chuàng)建和初始化MainActivity類(包括一系列的測(cè)量、布局析孽、繪制)搭伤,最后顯示在界面上。
2. 熱啟動(dòng)
啟動(dòng)app時(shí)袜瞬,后臺(tái)已有app的進(jìn)程(例:按back鍵怜俐、home鍵,應(yīng)用雖然會(huì)退出邓尤,但是該應(yīng)用的進(jìn)程是依然會(huì)保留在后臺(tái)拍鲤,可進(jìn)入任務(wù)列表查看),所以在已有進(jìn)程的情況下汞扎,這種啟動(dòng)會(huì)從已有的進(jìn)程中來啟動(dòng)應(yīng)用季稳,這個(gè)方式叫熱啟動(dòng)。
熱啟動(dòng)因?yàn)闀?huì)從已有的進(jìn)程中來啟動(dòng)澈魄,所以熱啟動(dòng)就不會(huì)走Application這步了景鼠,而是直接走M(jìn)ainActivity(包括一系列的測(cè)量、布局痹扇、繪制)铛漓,所以熱啟動(dòng)的過程只需要?jiǎng)?chuàng)建和初始化一個(gè)MainActivity就行了,而不必創(chuàng)建和初始化Application鲫构,因?yàn)橐粋€(gè)應(yīng)用從新進(jìn)程的創(chuàng)建到進(jìn)程的銷毀浓恶,Application只會(huì)初始化一次。
3. 溫啟動(dòng)
介于冷啟動(dòng)和熱啟動(dòng)之間, 一般來說在以下兩種情況下發(fā)生:
用戶back退出了App, 然后又啟動(dòng). App進(jìn)程可能還在運(yùn)行, 但是activity需要重建结笨。用戶退出App后, 系統(tǒng)可能由于內(nèi)存原因?qū)pp殺死, 進(jìn)程和activity都需要重啟, 但是可以在onCreate中將被動(dòng)殺死鎖保存的狀態(tài)(saved instance state)恢復(fù)包晰。
通過三種啟動(dòng)狀態(tài)的相關(guān)描述, 可以看出我們要做的啟動(dòng)優(yōu)化其實(shí)就是針對(duì)冷啟動(dòng). 熱啟動(dòng)和溫啟動(dòng)都相對(duì)較快.
二湿镀、app啟動(dòng)流程
這里是冷啟動(dòng)流程。在安卓系統(tǒng)上杜窄,應(yīng)用在沒有進(jìn)程的情況下肠骆,應(yīng)用的啟動(dòng)都是這樣一個(gè)流程:當(dāng)點(diǎn)擊app的啟動(dòng)圖標(biāo)時(shí),安卓系統(tǒng)會(huì)從Zygote進(jìn)程中fork創(chuàng)建出一個(gè)新的進(jìn)程分配給該應(yīng)用塞耕,之后會(huì)依次創(chuàng)建和初始化Application類蚀腿、創(chuàng)建MainActivity類、加載主題樣式Theme中的windowBackground等屬性設(shè)置給MainActivity以及配置Activity層級(jí)上的一些屬性扫外、再inflate布局莉钙、當(dāng)onCreate/onStart/onResume方法都走完了后最后才進(jìn)行contentView的measure/layout/draw顯示在界面上,所以直到這里筛谚,應(yīng)用的第一次啟動(dòng)才算完成磁玉,這時(shí)候我們看到的界面也就是所說的第一幀。
00=>start: Application的構(gòu)造器方法
01=>operation: attachBaseContext()
02=>operation: onCreate方法
03=>operation: Activity 的構(gòu)造方法
04=>operation: onCreate()
05=>operation: 配置主題背景等屬性
06=>operation: onStart()
07=>operation: onResume()
08=>end: 測(cè)量布局繪制顯示在界面上
00->01->02->03->04->05->06->07->08->09
三驾讲、測(cè)量app啟動(dòng)時(shí)間
應(yīng)用的啟動(dòng)時(shí)間:點(diǎn)擊應(yīng)用icon開始創(chuàng)建出一個(gè)新的進(jìn)程直到看到了界面上的第一幀蚊伞,這段時(shí)間就是應(yīng)用的啟動(dòng)時(shí)間。
1.方式一:通過adb shell命令的方式進(jìn)行測(cè)量吮铭,這種方法測(cè)量的最為精準(zhǔn)
$ adb shell am start -W [packageName]/[packageName.LaunchActivity]
執(zhí)行成功后返回三個(gè)測(cè)量得到的時(shí)間:
- **ThisTime : **一般和TotalTime時(shí)間一樣时迫,除非在應(yīng)用啟動(dòng)時(shí)開了一個(gè)透明的Activity預(yù)先處理一些事再顯示出主Activity,這樣將比TotalTime小谓晌。
- **TotalTime : **應(yīng)用的啟動(dòng)時(shí)間掠拳,包括創(chuàng)建進(jìn)程+Application初始化+Activity初始化到界面顯示。
- **WaitTime : **一般比TotalTime大點(diǎn)纸肉,包括系統(tǒng)影響的耗時(shí)溺欧。
舉個(gè)栗子,下面是對(duì)全視頻的啟動(dòng)時(shí)間測(cè)量:
冷啟動(dòng)時(shí)間:
C:\Users\luguoqiang>adb shell am start -W com.qsp.launcher/com.qsp.launcher.Spla
shActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.L
AUNCHER] cmp=com.qsp.launcher/.SplashActivity }
Status: ok
Activity: com.qsp.launcher/.SplashActivity
ThisTime: 957
TotalTime: 957
WaitTime: 977
Complete
熱啟動(dòng)時(shí)間:
C:\Users\luguoqiang>adb shell am start -W com.qsp.launcher/com.qsp.launcher.Spla
shActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.L
AUNCHER] cmp=com.qsp.launcher/.SplashActivity }
Status: ok
Activity: com.qsp.launcher/.SplashActivity
ThisTime: 284
TotalTime: 284
WaitTime: 312
Complete
2. 方式二:利用Traceview
性能分析工具
生成trace
文件
Traceview是一個(gè)性能分析工具, 主要是分析當(dāng)前線程情況, 各個(gè)方法執(zhí)行時(shí)間等柏肪,Android studio
內(nèi)置的工具姐刁。
在Application
的onCreate
開始和結(jié)尾打上trace
Debug.startMethodTracing("App");
...
Debug.stopMethodTracing();
運(yùn)行程序,會(huì)在設(shè)備的sd卡上生成一個(gè)App.trace
文件预吆,注意不要忘記加權(quán)限龙填。
DDMS中進(jìn)行分析
在下方的方法區(qū)點(diǎn)擊"Real Time/Call", 按照方法每次調(diào)用耗時(shí)降序排.
耗時(shí)超過500ms都是值得注意的。
3. 方式三:Time to initial display
從 4.4 (API 19) 開始拐叉,logcat會(huì)輸出帶有 Diaplay 的 log 岩遗。該值代表從app啟動(dòng)進(jìn)程到完成Activity第一次繪制的時(shí)候完成了下一個(gè)劉流程:
舉個(gè)栗子,全視頻的啟動(dòng)時(shí)間的如下
4.方式四:NimbleDroid
Android 應(yīng)用性能分析服務(wù)
四凤瘦、怎樣優(yōu)化
Application
的 onCreate()
中加入了許多耗時(shí)的操作宿礁,把這些初始化的操作放在一個(gè)單獨(dú)的線程中處理。
1. 利用InitializeService
的IntentService
來做初始化工作
//TODO IntentService
不同于Service
蔬芥,具體不同需要搜索資料
[InitializeService.java]
public class InitializeService extends IntentService {
private static final String ACTION_INIT_WHEN_APP_CREATE = "com.demo.service.action.INIT";
public InitializeService() {
super("InitializeService");
}
public static void start(Context context) {
Intent intent = new Intent(context, InitializeService.class);
intent.setAction(ACTION_INIT_WHEN_APP_CREATE);
context.startService(intent);
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_INIT_WHEN_APP_CREATE.equals(action)) {
performInit();
}
}
}
private void performInit() {
AppLog.d("performInit begin:" + System.currentTimeMillis());
// 這里進(jìn)行一些耗時(shí)性的初始化,需要注意某些初始化的操作不能在子線程中執(zhí)行梆靖。
...
// init crash helper
CrashHelper.init(this.getApplicationContext());
// init Push
PushPlatform.init(this.getApplicationContext());
...
AppLog.d("performInit end:" + System.currentTimeMillis());
}
}
Application
中的onCreate()
處用法如下
public class MyApplication extends MultiDexApplication {
@Override
public void onCreate() {
super.onCreate();
...
InitializeService.start(this);
}
}
五控汉、啟動(dòng)頁開啟前的白屏or黑屏優(yōu)化
初始化首屏的Activity過程中(或者說在等待第一幀顯示的時(shí)間里)屏幕會(huì)顯示一個(gè)空白/黑色的窗口(顏色基于主題),直至首屏Activity完全啟動(dòng)返吻。
這個(gè)空白的窗口跟主題相關(guān)姑子,所以可以從首屏的主題入手。
給Splash界面加上一個(gè)主題测僵,這個(gè)主題會(huì)在現(xiàn)實(shí)第一幀前提前顯示在界面上街佑,設(shè)置想要展示的背景。
做一個(gè)splash背景
[logo_splash.xml]
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 底層白色 -->
<item android:drawable="@color/white" />
<!-- 頂層Logo居中 -->
<item>
<bitmap
android:gravity="center"
android:src="@drawable/ic_github" />
</item>
</layer-list>
自定義splashTheme
主題
[styte.xml]
<style name="SplashTheme" parent="AppTheme">
<item name="android:windowBackground">@drawable/logo_splash</item>
</style>
將一個(gè)什么不渲染的布局的Activity作為啟動(dòng)頁
public class LogoSplashActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 注意, 這里并沒有setContentView, 單純只是用來跳轉(zhuǎn)到相應(yīng)的Activity.
// 目的是減少首屏渲染
if (AppPref.isFirstRunning(this)) {
IntroduceActivity.launch(this);
}
else {
MainActivity.launch(this);
}
finish();
}
}
在AndroidMainfest.xml
設(shè)置為啟動(dòng)屏捍靠,并加上主題
<activity
android:name=".ui.module.main.LogoSplashActivity"
android:screenOrientation="portrait"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
參考博客:
(TraceView 性能分析工具)[http://mouxuejie.com/blog/2016-02-25/android-tools-traceview/]