簡介
性能優化的目的不是爲了優化而優化,而且爲了以後不再優化, 給自己統一 一個標準。
這裏也許會有人問 APP 啓動還需要優化嗎?啓動又不是我們自己寫的代碼,難道 Google 工程師會犯這麼低級的錯嗎?其實這還真不是 Google 的錯,應該說是給我們開發者留了一個坑吧。應該有的同學知道是怎麼一回事兒了,當我們在系統桌面任意點擊一個 APP 是不是會發現啓動的時候有一瞬間有白屏出現(以前老版本是黑屏) 那麼我們怎麼來優化這個黑白屏的問題勒,現在我們先來了解一下 Android 手機重開機到啓動 APP 的過程吧。
APP 啓動流程
這裏會設計到 Android 系統源碼的知識,但並不會深入解析源碼,我們只是瞭解一個過程,因爲太深入我自己也懵。
系統的啓動
我在這裏大致分爲了 6 個步驟,下面以流程圖爲準
啓動步驟
- 首先拿到一部 Android 系統的手機打開電源,引導芯片代碼加載引導程序 BootLoader 到 RAM 中去執行。
- BootLoader 把操作系統拉起來。
- Linux 內核啓動開始系統設置,找到一個 init.rc 文件啓動初始化進程。
- init 進程初始化和啓動屬性服務,之後開啓 Zygote 進程。
- Zygote 開始創建 JVM 並註冊 JNI 方法,開啓 SystemServer。
- 啓動 Binder 線程池和 SystemServiceManager,並啓動各種服務。
Launcher 啓動
App Appcation 啓動
- 手機回到系統桌面, 通過 adb shell dumpsys window w |findstr / |findstr name= 來查看當前的進程和 Activity 名。
- 當點擊桌面 APP 圖標的時候會走 Launcher . java 的 onClick (View view) 方法,詳細見下圖。
startActivity(intent) 會開啓一個 APP 進程
- AcitivityThread main() 調用執行流程,見下圖。
**最後 ActivityThread main() 是通過反射來進行初始化的**
- ActivityThread.java 做爲入口,詳細解說 main() 函數,還是以一個動畫來演示一下吧;
根據上面的動畫,大家應該已經明白 ActivityThread.java main() 方法中 Appcation onCreate() 的是怎麼被調用起來的吧。
注意:
不知道大家有沒有注意 ActivityThread main() 中 Looper.prepareMainLooper(); 其實咱們爲什麼能夠在 Main Thread 中創建 Handler 不會報錯了吧,是因爲 Activity 啓動的時候在這裏已經默認開啓了 Looper。
APP 啓動黑白屏問題
終於到了正題了,下面我們就來說下啓動黑白屏的問題,還是先來看一個 GIF 吧。
市面上 APP 黑白屏
從上面的一段錄屏我們可以發現市面上常見的 APP 啓動有的是白屏有的是做了優化。黑屏只有在 Android 4.n 具體是哪個版本我也忘了。那麼現在我們就以我現在的真實項目來優化一下啓動。
真實項目中優化
簡介
首先爲什麼會造成白屏勒我們來看一段源碼
最後就是這個 windowBackground 搞的鬼,知道了是這個搞的鬼那麼我們就可以來進行優化了。
優化方案 一
在自己的 AppTheme 中加入 windowBackground
優化方案 二
設置 windowbackgroud 爲透明的
<item name="android:windowIsTranslucent">true</item>
但是:
這 2 中方法會有一個問題,就是所有的 Activity 啓動都會顯示。
優化方案 三
- 單獨做成一個 AppTheme.Launcher
<style name="AppTheme.Launcher">
<item name="android:windowFullscreen">true</item>
<!--<item name="android:windowDisablePreview">true</item>-->
<item name="android:windowBackground">@color/colorAccent</item>
</style>
-
在清單文件中 啓動 Activity 加入該 主題
<activity android:name="com.t01.android.dida_login.mvp.ui.activity.LoginActivity" android:configChanges="keyboardHidden|orientation|screenSize" android:theme="@style/AppTheme.Launcher" android:windowSoftInputMode="adjustUnspecified|stateHidden"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
-
在啓動 Activity 頁面中加入
setTheme(R.style.AppTheme_Launcher);
最後這樣做只有啓動的 UI 才能見到自己的樣式
-
最後效果,因爲我這裏沒有背景圖,故弄了一個主題顏色,如果想要設置一張背景圖片可以參考下面的示例,不然有可能會引起圖片拉伸效果。
我這裏啓動時間大概在 500 ms ~ 800 ms 左右。
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<bitmap android:src="@mipmap/app_bg"
android:gravity="fill"/>
</item>
</layer-list>
最後在清單 啓動 Activity 的 Theme 中修改爲
<item name="android:windowBackground">@drawable/app_theme_bg</item>
-
據說 QQ 的實現方法是(這裏只做參考,感興趣的同學可以自己試試。)
<item name="android:windowDisablePreview">true</item> <item name="android:windowBackground">@null</item>
啓動時間查看
4.4 以前版本查看
adb shell am start -W packName/activity 全路徑
4.4 版本以後查看方式
通過關鍵字 Displayed 並篩選爲 No Filters
2019-04-25 18:35:57.629 508-629/? I/ActivityManager: Displayed com.lingyi.autiovideo.lykj/com.t01.android.dida_login.mvp.ui.activity.LoginActivity: +844ms
工具分析代碼執行
Appcation 中查看耗時通過(如果有的同學還用 Log 打印系統時間來相減來查看 耗時的話,看完我這篇文章就可以換成下面方法了,不然就有點 LOW 了哈)
//開始計時
Debug.startMethodTracing(filePath);
中間爲需要統計執行時間的代碼
//停止計時
Debug.stopMethodTracing();
還是通過一組動畫來看我怎麼操作的吧。(注意這裏的時間是 微妙 微妙/10^6 = s 應該是這樣,忘了)
這個工具可以很友好的提示每個函數具體在內部執行了多少時間,卡頓其實也可以用這個方法來進行監測
導出 trace 文件命令
adb pull /storage/emulated/0/appcation_launcher_time.trace
我這裏耗時還不算太大 大概在 0.2 - 0.3 s 左右。
Appcation 中優化方案(並不絕對,優化思路差不多)
- 開子線程
- 線程中沒有創建 Handler、沒有操作 UI 、對異步要求不高
- 懶加載
- 用到的時候在初始化,如網絡,數據庫,圖片庫,或一些三方庫。
- 使用 IntentService onHandleIntent () 方法來進行初始化一些比較耗時的操作
總結
最後啓動優化可以配合上面的 3 點優化方案 + Appcation 優化方案 = 你自己最優方案。
學習分享,共勉
題外話,我從事Android開發已經五年了,此前我指導過不少同行。但很少跟大家一起探討,正好最近我花了一個多月的時間整理出來一份包括不限於高級UI、性能優化、移動架構師、NDK、混合式開發(ReactNative+Weex)微信小程序、Flutter等全方面的Android進階實踐技術,今天暫且開放給有需要的人,若有關於此方面可以轉發+關注+點贊後加羣 878873098 領取,或者評論與我一起交流探討。
資料免費領取方式:轉發+關注+點贊後,加入點擊鏈接加入羣聊:Android高級開發交流羣(878873098)即可獲取免費領取方式!
重要的事說三遍,關注!關注!關注!