一個應用App的啟動速度能夠影響用戶的首次體驗,啟動速度較慢(感官上)的應用可能導致用戶再次開啟App的意圖下降,或者卸載放棄該應用程序
本文將從兩個方向優(yōu)化應用的啟動速度 :
1.視覺體驗優(yōu)化
2.代碼邏輯優(yōu)化
應用程序啟動有三種狀態(tài),每種狀態(tài)都會影響應用程序對用戶可見所需的時間:冷啟動,熱啟動和溫啟動
關于這3種啟動方式,可以查看谷歌開發(fā)文檔
大家常說的是冷啟動和熱啟動
1.冷啟動:當啟動應用時,后臺沒有該應用的進程,這時系統(tǒng)會重新創(chuàng)建一個新的進程分配給該應用,這個啟動方式就是冷啟動
2.熱啟動:當啟動應用時,后臺已有該應用的進程(例:按back鍵、home鍵,應用雖然會退出,但是該應用的進程是依然會保留在后臺,可進入任務列表查看),所以在已有進程的情況下,這種啟動會從已有的進程中來啟動應用,這個方式叫熱啟動
在冷啟動時,應用程序從頭開始。在其他狀態(tài)下,系統(tǒng)需要將正在運行的應用程序從后臺運行到前臺。我們建議您始終根據(jù)冷啟動的假設進行優(yōu)化。這樣做也可以改善熱啟動和溫啟動的性能
在冷啟動開始時,系統(tǒng)有三個任務。這些任務是:
1.加載并啟動應用程序
2.啟動后立即顯示應用程序空白的啟動窗口
3.創(chuàng)建應用程序進程
一旦系統(tǒng)創(chuàng)建應用程序進程,應用程序進程就會負責下一階段,這些階段包括:
1.創(chuàng)建app對象
2.啟動主線程(main thread)
3.填充加載布局Views
4.在屏幕上執(zhí)行View的繪制過程.measure -> layout -> draw
應用程序進程完成第一次繪制后,系統(tǒng)進程會交換當前顯示的背景窗口,將其替換為主活動。此時,用戶可以開始使用該應用程序
因為App應用進程的創(chuàng)建過程是由手機的軟硬件決定的,所以我們只能在這個創(chuàng)建過程中視覺優(yōu)化
所謂的主題優(yōu)化,就是應用程序在冷啟動的時候(1~2階段),設置啟動窗口的主題
因為現(xiàn)在 App 應用啟動都會先進入一個閃屏頁(LaunchActivity) 來展示應用信息
默認情況下會出現(xiàn)白屏現(xiàn)象,系統(tǒng)默認會在啟動應用程序的時候 啟動空白窗口 ,直到 App 應用程序的入口 Activity 創(chuàng)建成功,視圖繪制完畢系統(tǒng)默認會在啟動應用程序的時候 啟動空白窗口 ,直到 App 應用程序的入口 Activity 創(chuàng)建成功,視圖繪制完畢系統(tǒng)默認會在啟動應用程序的時候 啟動空白窗口 ,直到 App 應用程序的入口 Activity 創(chuàng)建成功,視圖繪制完畢
解決方法可查看Android啟動界面SplashActivit的實現(xiàn)方法
根據(jù)上面啟動時間的輸出統(tǒng)計,我們就可以先記錄優(yōu)化前的冷啟動耗時,然后再對比優(yōu)化之后的啟動時間
Application 作為 應用程序的整個初始化配置入口,時常擔負著它不應該有的負擔~
有很多第三方組件(包括App應用本身)都在 Application 中搶占先機,完成初始化操作
但是在 Application 中完成繁重的初始化操作和復雜的邏輯就會影響到應用的啟動性能
通常,有機會優(yōu)化這些工作以實現(xiàn)性能改進,這些常見問題包括:
1.復雜繁瑣的布局初始化
2.阻塞主線程 UI 繪制的操作,如 I/O 讀寫或者是網絡訪問
3.Bitmap 大圖片或者 VectorDrawable加載
4.其它占用主線程的操作
我們可以根據(jù)這些組件的輕重緩急之分,對初始化做一下分類
1.必要的組件一定要在主線程中立即初始化(入口 Activity 可能立即會用到)
2.組件一定要在主線程中初始化,但是可以延遲初始化
3.組件可以在子線程中初始化
**放在子線程的組件初始化建議延遲初始化 **,這樣就可以了解是否會對項目造成影響
所以對于上面的分析,我們可以在項目中 Application 的加載組件進行如下優(yōu)化 :
new Thread(new Runnable() {
@Override
public void run() {
//設置線程的優(yōu)先級,不與主線程搶資源
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//子線程初始化第三方組件
Thread.sleep(5000);//建議延遲初始化,可以發(fā)現(xiàn)是否影響其它功能,或者是崩潰!
}
}).start();
handler.postDelayed(new Runnable() {
@Override
public void run() {
//延遲初始化組件
}
}, 3000);
最后還剩下那些為數(shù)不多的組件在主線程初始化動作,例如埋點,點擊流,數(shù)據(jù)庫初始化等,不過這些消耗的時間可以在其它地方相抵
需求背景: 應用App通常會設置一個固定的閃屏頁展示時間,例如2000ms,所以我們可以根據(jù)用戶手機的運行速度,對展示時間做出調整,但是總時間仍然為 2000ms
閃屏頁展示總時間 = 組件初始化時間 + 剩余展示時間
也就是2000ms的總時間,組件初始化了800ms,那么就再展示1200ms即可
Application 初始化后會調用 attachBaseContext() 方法,再調用 Application 的 onCreate(),再到入口 Activity的創(chuàng)建和執(zhí)行 onCreate() 方法。所以我們就可以在 Application 中記錄啟動時間
//Application
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
SPUtil.putLong("application_attach_time", System.currentTimeMillis());//記錄Application初始化時間
}
有了啟動時間,我們得知道入口的 Acitivty 顯示給用戶的時間(View繪制完畢),在onWindowFocusChanged()的回調時機中表示可以獲取用戶的觸摸時間和View的流程繪制完畢,所以我們可以在這個方法里記錄顯示時間
//Activity
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
long appAttachTime = SPUtil.getLong("application_attach_time");
long diffTime = System.currentTimeMillis() - appAttachTime;//從application到入口Acitity的時間
//所以閃屏頁展示的時間為 2000ms - diffTime.
}
所以我們就可以動態(tài)的設置應用閃屏的顯示時間,盡量讓每一部手機展示的時間一致,這樣就不會讓手機配置較低的用戶感覺漫長難熬的閃屏頁時間(例如初始化了2000ms,又要展示2000ms的閃屏頁時間.),優(yōu)化用戶體驗
閃屏頁過后就要展示金主爸爸們的廣告頁了
因為項目中廣告頁圖片有可能是大圖,APng動態(tài)圖片,所以需要將這些圖片下載到本地文件,下載完成后再顯示,這個過程往往會遇到以下兩個問題 :
因為不清楚用戶的網絡環(huán)境,有些用戶下載廣告頁可能需要一段時間,這時候又不可能無限的等候。所以針對這個問題我們可以開啟 IntentService 用來下載廣告頁圖片
在入口 Acitivity 中開啟 IntentService 來下載廣告頁。 或者是其它異步下載操作
在廣告頁圖片 文件流完全寫入后 記錄圖片大小,或者記錄一個標識
在下次的廣告頁加載中可以判斷是否已經下載好了廣告頁圖片以及圖片是否完整,否則刪除并且再次下載圖片
另外因為在閃屏頁中仍然有 剩余展示時間,所以在這個時間段里如果用戶已經下載好了圖片并且圖片完整,就可以顯示廣告頁。否則進入主 Activity , 因為 IntentService 仍然在后臺繼續(xù)默默的下載并保存圖片~