朝陽楊少爺(ID:CY_YANG_DA_YE),專一於Android領域的開發者、分享者。複製代碼
咱們的APP新版本,從2017年4月份提交第一行代碼開始,就如今已經有兩年半的時間,在這兩年多的時間裏,APP的內容內容不斷豐富,例如前後加入了求職招聘、問答、我的中心、二手機,小視頻等等模塊。同時對於之前的舊功能也在不斷地完善,例如,豐富了發帖的內容、小視頻詳情頁像抖音同樣方便快捷、標籤的聚合更加精準的吸引用戶。java
在功能、內容豐富的同時,難免會引入不少第三方的工具,例如友盟、個推、神策、騰訊雲的視頻組件、IM及時通信組件,GreenDao數據庫等等shell
隨着代碼量愈來愈多,第三方工具越來多的致使APP,首次啓動的時候,時間較長,用戶體驗較差。數據庫
Nimbledroid統計Google Play各種別APP冷啓動平均耗時 (Nexus 5 + Android 4.4)
[圖片上傳失敗...(image-c82b9a-1565334335822)]
而Google Play排名前100的非遊戲類的應用的冷啓動時間統計爲緩存
39個冷啓動時間在2秒之內
73個冷啓動時間在3秒之內ruby
而咱們APP的冷啓動時間爲:bash
執行命令行網絡
adb shell am start -W -n 包名/包名.activity.MainEntryActivity
複製代碼
ThisTime:最後一個啓動的Activity的啓動耗時;
TotalTime:本身的全部Activity的啓動耗時;
WaitTime: ActivityManagerService啓動App的Activity時的總時間(包括當前Activity的onPause()和本身Activity的啓動)。app
也就是說,在啓動的時候,須要2.4s。異步
可見,還有很大的優化空間。ide
接下來,就經過本篇內容,深刻的剖析一下,啓動慢的緣由,並給出合理的解決方案。
開始,優化啓動的過程,那麼就要先了解一下,Android系統,在啓動一個APP的過程是什麼樣的?都作了什麼事兒?
冷啓動:當啓動應用時,後臺沒有該應用的進程,這時系統會從新建立一個新的進程分配給該應用。
熱啓動:當啓動應用時,後臺存在該應用的進程(back鍵,home鍵,應用退出,可是沒有銷燬),從已有的進程中啓動。
由於熱啓動是後臺進程已經存在了,因此啓動速度比較快,這裏提到的優化是指冷啓動。
Android冷啓動過程相對比較複雜,須要經歷35步,簡單來講,須要5個過程
整個應用程序的啓動過程要執行不少步驟,可是總體來看,主要分爲如下五個階段:
一. Step1 - Step 11:
Launcher經過Binder進程間通訊機制通知ActivityManagerService,
它要啓動一個Activity;
二. Step 12 - Step 16:
ActivityManagerService經過Binder進程間通訊機制通知Launcher進入Paused狀態;
三. Step 17 - Step 24:
Launcher經過Binder進程間通訊機制通知ActivityManagerService,它已經準備就緒進入Paused狀態,
因而ActivityManagerService就建立一個新的進程,用來啓動一個ActivityThread實例,
即將要啓動的Activity就是在這個ActivityThread實例中運行;
四. Step 25 - Step 27:
ActivityThread經過Binder進程間通訊機制將一個ApplicationThread類型的Binder對象傳遞給ActivityManagerService,
以便之後ActivityManagerService可以經過這個Binder對象和它進行通訊;
五. Step 28 - Step 35:
ActivityManagerService經過Binder進程間通訊機制通知ActivityThread,
如今一切準備就緒,它能夠真正執行Activity的啓動操做了。
複製代碼
在更加簡單一點,從用戶點擊桌面圖標開始,大體會經歷如下幾個過程:
在這個啓動過程中,可能會存在的幾個問題:
1.點擊圖標好久都不響應。
2.首頁顯示太慢。
3.首頁顯示後沒法操做。
而接下來,重點解決的問題就是,點擊圖標好久都不響應的問題。
優化工具
當,遇到一個問題的時候,到最後解決的步驟,無外乎有如下幾個步驟。
Hugo是 jake Wharton大神製做的工具,能夠在log當中打印每一個方法的執行時間,能夠幫咱們定位到運行時間比較長的方法和函數,配置也相對簡單。
接下來,就在代碼當中實踐一下。
本想觀察一下 Application當中,初始化的時間
@DebugLog
@Override
public void onCreate() {
super.onCreate();
initHttpClient();
// //初始化配置
initGlobal();
// CrashCaptureHandler.getInstance().initCrashHandler();
initJPush();
initGetTuiPush();
initSource();
initSmallVideoRecord();
initIM();
if (BuildConfig.DEBUG) {
boolean hostUrlEnable = new HostUrlUtils().initHost(URLConstants.class.getDeclaredFields(), URLConstants.RELEASE_SERVER);
if (hostUrlEnable) {
Dispatcher.hostUrlClass = URLConstants.class;
}
} else {
//正式環境纔開始crash防禦
install();
initAliyunLog();
}
initHuoDongHeZi();
initSensor();
initUmeng();
initLinkMe();
initRN();
}
複製代碼
觀察打印的Log
2019-08-10 16:10:09.752 30634-30634/> V/MyApplication: ⇢ onCreate()
2019-08-10 16:10:09.941 30678-30678/? V/MyApplication: ⇢ onCreate()
2019-08-10 16:10:10.004 30692-30692/? V/MyApplication: ⇢ onCreate()
2019-08-10 16:10:10.124 30737-30737/? V/MyApplication: ⇢ onCreate()
2019-08-10 16:10:10.529 30678-30678/? V/MyApplication: ⇠ onCreate [587ms]
2019-08-10 16:10:10.569 30634-30634/? V/MyApplication: ⇠ onCreate [816ms]
2019-08-10 16:10:10.832 30737-30737/? V/MyApplication: ⇠ onCreate [707ms]
2019-08-10 16:10:10.881 30692-30692/? V/MyApplication: ⇠ onCreate [877ms]
2019-08-10 16:10:13.917 31109-31109/?:xg_service_v3 V/MyApplication: ⇢ onCreate()
2019-08-10 16:10:14.441 31109-31109/?:xg_service_v3 V/MyApplication: ⇠ onCreate [524ms]
複製代碼
能夠發現,原來初始化onCreat被屢次調用!
正常狀況下,一個應用會開啓一個進程,那麼application會被執行一次,說明在啓動的過程中,不只如此,由於初始化的重複調用,致使初始化的時間就有 2391ms!這就極大的影響到APP的啓動速度!給用於一種,點擊完圖標沒有反應的感受。
既然,問題已經發現了,就要解決這些問題。
既然發現是進程的問題,那麼就要看看,在啓動的過程中,總共有哪些進程。
2019-08-10 16:40:07.881 3166-3166/? D/yzc: ?
2019-08-10 16:40:07.978 3225-3225/? D/yzc: ?:pushcore
2019-08-10 16:40:08.128 3298-3298/? D/yzc: ?:ipc
2019-08-10 16:40:08.172 3245-3245/? D/yzc: ?:pushservice
2019-08-10 16:40:11.754 3739-3739/?s:xg_service_v3 D/yzc: ?s:xg_service_v3
複製代碼
從中,能夠看到除了APP的進程以外,還有 信鴿推送的進程,ipc進程,個推動程,極光的進程。
以前可能某些業務須要,集成了三家的推送,而每一個推送都會在後臺默默的開啓一個進程來接收推送消息。天然就會致使APP的第一次初始化啓動很慢。
和運營小夥伴確認,當前只有信鴿推送在使用,個推,極光就給去掉。那麼去掉以後,再看一下啓動時間。
同時也避免了屢次初始化的問題!
相比以前的2391ms 優化了800ms! 接下來,繼續優化,看看哪裏還有能夠優化的空間。
在Application當中,能夠看到,初始化的內容有
initGlobal();
初始化配置
initIM();
初始化及時通信
initSource();
初始化APP相關版本配置
initSmallVideoRecord();
初始化短視頻
install();
初始化放在Crash
initAliyunLog();
初始化阿里雲相關內容
initHuoDongHeZi();
初始化活動盒子
initSensor();
初始化神策
initUmeng();
初始化友盟
initLinkMe();
初始化深度鏈接
initRN();
初始化ReactNative
分別統計一下每一個初始化須要的的時間。
initBbsGlobal [73ms]
initIM [95ms]
initSource [0ms]
initSmallVideoRecord [11ms]
install [0ms]
initAliyunLog [19ms]
initHuoDongHeZi [69ms]
initSensor [191ms]
initUmeng [14ms]
initLinkMe [63ms]
initRN [0ms]
根據統計,能夠看到耗時較長的方法有神策、IM聊天、活動盒子、深度鏈接、配置文件。
對於這些時間長的,在不影響功能的狀況下,能夠嘗試放在子線程當中進行初始化,不佔用主線程的資源。
new Thread(new Runnable() {
@Override
public void run() {
initIM();
initSource();
initSmallVideoRecord();
initLinkMe();
initUmeng();
if (!URLConstants.RELEASE_SERVER) {
boolean hostUrlEnable = new HostUrlUtils().initHost(URLConstants.class.getDeclaredFields(), URLConstants.RELEASE_SERVER);
if (!hostUrlEnable) {
new AlertDialog.Builder(getApplicationContext())
.setTitle("未檢測到可用的URL")
.setMessage("請確診URL地址可用")
.setPositiveButton("退出", null).show();
}
Dispatcher.hostUrlClass = URLConstants.class;
} else {
//正式環境纔開始crash防禦
install();
initAliyunLog();
}
initHuoDongHeZi();
}
}).start();
複製代碼
通過改造,看一下啓動時間。
相比以前又優化了 300ms!,相比沒有優化以前優化了1132ms! 也就是快了 1秒 多的時間,能夠說效果仍是十分明顯的。
同時能夠看到,Application的初始化時間當前僅需199ms
在 Application的優化告一段落,關注一下 MainEntryActivity 這個 Activity 是進入app的一個Activity,在這個 Activity的onCreate()當中 進行了一些請求操做,看看這裏面有什麼能夠優化的地方。
在這個裏,有兩個操做形成耗時時間比較長,一個是
isCityInviteNetWork()
這個方法是當用戶登陸的時候,根據用戶獲取到用戶的地理位置信息並存儲。
這個方法徹底能夠放在子線程當中,不佔用主線程的資源。
checkAdversiterment()
這個是在網絡獲取開屏廣告的方法,這個須要請求接口,獲取到開屏廣告並展現的方法。
對於這個方法,也能夠子線程當中異步操做,第一次請求下來緩存起來,從第二次進入APP,在進行展現,這樣避免了主線程的耗時操做。
通過這些操做以後,能夠看到啓動時間,已經優化到了1s左右。
雖然,沒有目前沒有達到秒開的效果,可是比起優化以前已經有了很大的區別,優化效果。從此,還會繼續優化,爭取達到一個更好的效果。
以前我看有朋友說能夠設置主題達到秒開的效果,我試了一下好像不太行,也須要大概500ms左右,若是你們有好的想法和建議,歡迎給我留言!咱們一塊兒討論!
參考:
www.jianshu.com/p/e69d22ec0…
www.jianshu.com/p/1e65aa223…
www.jianshu.com/p/496529bd1…
blog.csdn.net/u013263323/…