Android 分析實(shí)現(xiàn)性能優(yōu)化之啟動(dòng)速度優(yōu)化
本文主要探討以下幾個(gè)問(wèn)題:
- 啟動(dòng)方式
- 啟動(dòng)流程中可優(yōu)化的環(huán)節(jié)
- 檢測(cè)工具
- 優(yōu)化點(diǎn)
- 黑白屏問(wèn)題
啟動(dòng)方式
應(yīng)用有三種啟動(dòng)狀態(tài),每種狀態(tài)都會(huì)影響應(yīng)用向用戶(hù)顯示所需的時(shí)間:冷啟動(dòng)、溫啟動(dòng)與熱啟動(dòng)
冷啟動(dòng)(啟動(dòng)優(yōu)化目標(biāo))
冷啟動(dòng)是指應(yīng)用從頭開(kāi)始啟動(dòng):系統(tǒng)進(jìn)程在冷啟動(dòng)后才創(chuàng)建應(yīng)用進(jìn)程。發(fā)生冷啟動(dòng)的情況包括應(yīng)用自設(shè)備啟動(dòng)后或系統(tǒng)終止應(yīng)用后首次啟動(dòng)。
熱啟動(dòng)
在熱啟動(dòng)中,系統(tǒng)的所有工作就是將 Activity 帶到前臺(tái)。只要應(yīng)用的所有 Activity 仍駐留在內(nèi)存中,應(yīng)用就不必重復(fù)執(zhí)行對(duì)象初始化、布局加載和繪制。
溫啟動(dòng)
溫啟動(dòng)包含了在冷啟動(dòng)期間發(fā)生的部分操作;同時(shí),它的開(kāi)銷(xiāo)要比熱啟動(dòng)高。有許多潛在狀態(tài)可視為溫啟動(dòng)。例如:
- 用戶(hù)在退出應(yīng)用后又重新啟動(dòng)應(yīng)用。進(jìn)程可能未被銷(xiāo)毀,繼續(xù)運(yùn)行,但應(yīng)用需要執(zhí)行 onCreate() 從頭開(kāi)始重新創(chuàng)建 Activity。
- 系統(tǒng)將應(yīng)用從內(nèi)存中釋放,然后用戶(hù)又重新啟動(dòng)它。進(jìn)程和 Activity 需要重啟,但傳遞到 onCreate() 的已保存的實(shí)例savedInstanceState對(duì)于完成此任務(wù)有一定助益。
- …
啟動(dòng)流程中可優(yōu)化的環(huán)節(jié)
啟動(dòng)流程中開(kāi)發(fā)者可優(yōu)化的環(huán)節(jié)不多,咱們可以從APP啟動(dòng)流程中尋找下

APP啟動(dòng)過(guò)程如下:
- 點(diǎn)擊桌面App圖標(biāo),Launcher進(jìn)程采用Binder IPC向system_server進(jìn)程發(fā)起startActivity請(qǐng)求;
- system_server進(jìn)程接收到請(qǐng)求后,向zygote進(jìn)程發(fā)送創(chuàng)建進(jìn)程的請(qǐng)求;
- Zygote進(jìn)程fork出新的子進(jìn)程,即App進(jìn)程;
- App進(jìn)程,通過(guò)Binder IPC向sytem_server進(jìn)程發(fā)起attachApplication請(qǐng)求;
- system_server進(jìn)程在收到請(qǐng)求后,進(jìn)行一系列準(zhǔn)備工作后,再通過(guò)binder IPC向App進(jìn)程發(fā)送scheduleLaunchActivity請(qǐng)求;
- App進(jìn)程的binder線(xiàn)程(ApplicationThread)在收到請(qǐng)求后,通過(guò)handler向主線(xiàn)程發(fā)送LAUNCH_ACTIVITY消息;
- 主線(xiàn)程在收到Message后,通過(guò)反射機(jī)制創(chuàng)建目標(biāo)Activity,并回調(diào)Activity.onCreate()等方法。
- 到此,App便正式啟動(dòng),開(kāi)始進(jìn)入Activity生命周期,執(zhí)行完onCreate/onStart/onResume方法,UI渲染結(jié)束后便可以看到App的主界面。
所以,我們能優(yōu)化的階段只有 Application.onCreate() —> Activity.onWindowFocusChanged()
檢測(cè)工具
啟動(dòng)時(shí)間檢測(cè)
啟動(dòng)的時(shí)間怎樣算是合適的?怎樣一個(gè)時(shí)間范圍內(nèi)用戶(hù)是感覺(jué)流暢的?Android Vitals在您的應(yīng)用出現(xiàn)以下情況時(shí)將其啟動(dòng)時(shí)間視為過(guò)長(zhǎng):
- 冷啟動(dòng)用了 5 秒或更長(zhǎng)時(shí)間
- 溫啟動(dòng)用了 2 秒或更長(zhǎng)時(shí)間
- 熱啟動(dòng)用了 1.5 秒或更長(zhǎng)時(shí)間
那APP啟動(dòng)用了多長(zhǎng)時(shí)間?用什么區(qū)檢測(cè)?
Logcat Displayed
在 Android 4.4(API 級(jí)別 19)及更高版本中,logcat 包含一個(gè)輸出行,其中包含名為 Displayed 的值。此值代表從啟動(dòng)進(jìn)程到在屏幕上完成對(duì)應(yīng) Activity 的繪制所用的時(shí)間。

adb 命令統(tǒng)計(jì)
adb shell am start -S -W [packageName]/[activityName]
C:\Users\****>adb shell
generic_x86_arm:/ $ am start -S -W com.miss.misslink/.MainActivity
Stopping: com.miss.misslink
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.miss.misslink/.MainActivity }
Status: ok
LaunchState: COLD
Activity: com.miss.misslink/.MainActivity
TotalTime: 941
WaitTime: 961
Complete
WaitTime:包括前一個(gè)應(yīng)用Activity pause的時(shí)間和新應(yīng)用啟動(dòng)的時(shí)間;
ThisTime:表示一連串啟動(dòng)Activity的最后一個(gè)Activity的啟動(dòng)耗時(shí);
TotalTime:表示新應(yīng)用啟動(dòng)的耗時(shí),包括新進(jìn)程的啟動(dòng)和Activity的啟動(dòng),但不包括前一個(gè)應(yīng)用Activity pause的耗時(shí)。

我們一般只用 TotalTime,可以很清楚的知道APP的啟動(dòng)時(shí)間。那我們?nèi)绾闻袛嗍悄男┓椒ê臅r(shí)太多導(dǎo)致APP啟動(dòng)時(shí)間過(guò)長(zhǎng)呢?
CPU profile
API level >= 26
要在應(yīng)用啟動(dòng)過(guò)程記錄CPU活動(dòng),需要做以下操作
1.依次選擇 Run > Edit Configurations

2.勾選 Trace Java Methods(跟蹤 Java 方法:在運(yùn)行時(shí)檢測(cè)應(yīng)用,以在每個(gè)方法調(diào)用開(kāi)始和結(jié)束時(shí)記錄一個(gè)時(shí)間戳。系統(tǒng)會(huì)收集并比較這些時(shí)間戳,以生成方法跟蹤數(shù)據(jù),包括時(shí)間信息和 CPU 使用率。)

3. 依次選擇 Run > Profile,將您的應(yīng)用部署到搭載 Android 8.0(API 級(jí)別 26)或更高版本的設(shè)備上
主要分析的地方有3個(gè): Flame Chart、 Top Down 、bottom up。

API level < 26
對(duì)于A(yíng)PI低于26的,我們可以調(diào)用 Debug API,調(diào)用起點(diǎn) Application構(gòu)造函數(shù)
public class MyApplication extends Application {
public MyApplication() {
// 沒(méi)有指定絕對(duì)路徑,就是相對(duì)路徑,相對(duì) sdcard
Debug.startMethodTracing("miss");
}
}
調(diào)用終點(diǎn) Activity.onWindowFocusChanged()
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
Debug.stopMethodTracing();
}
如此一來(lái)會(huì)在 sdcard 路徑下生成 miss 文件,雙擊打開(kāi)即可
StrictMode 嚴(yán)苛模式
StrictMode是一個(gè)開(kāi)發(fā)人員工具,它可以檢測(cè)出我們可能無(wú)意中做的事情,并將它們提請(qǐng)我們注意,以便我們能夠修復(fù)它們。StrictMode最常用于捕獲應(yīng)用程序主線(xiàn)程上的意外磁盤(pán)或網(wǎng)絡(luò)訪(fǎng)問(wèn)。幫助我們讓磁盤(pán)和網(wǎng)絡(luò)操作遠(yuǎn)離主線(xiàn)程,可以使應(yīng)用程序更加平滑、響應(yīng)更快
// Application onCreate 中使用
@Override
public void onCreate() {
if (BuildConfig.DEBUG) {
//線(xiàn)程檢測(cè)策略
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads() //讀、寫(xiě)操作
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.penaltyDeath()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects() //Sqlite對(duì)象泄露
.detectLeakedClosableObjects() //未關(guān)閉的Closable對(duì)象泄露
.penaltyLog() //違規(guī)打印日志
.penaltyDeath() //違規(guī)崩潰
.build());
}
優(yōu)化點(diǎn)
- 合理的使用異步初始化、延遲初始化、懶加載機(jī)制。
- 啟動(dòng)過(guò)程避免耗時(shí)操作,如數(shù)據(jù)庫(kù) I/O操作不要放在主線(xiàn)程執(zhí)行。
- 類(lèi)加載優(yōu)化:提前異步執(zhí)行類(lèi)加載。
- 合理使用IdleHandler進(jìn)行延遲初始化。
- 簡(jiǎn)化布局
- 第三方庫(kù)有些是有優(yōu)化插件的,比如ARouter
黑白屏問(wèn)題
當(dāng)系統(tǒng)加載并啟動(dòng) App 時(shí),需要耗費(fèi)相應(yīng)的時(shí)間,這樣會(huì)造成用戶(hù)會(huì)感覺(jué)到當(dāng)點(diǎn)擊 App 圖標(biāo)時(shí)會(huì)有 “延遲” 現(xiàn)象,為了解決這一問(wèn)題,Google 的做法是在 App 創(chuàng)建的過(guò)程中,先展示一個(gè)空白頁(yè)面,讓用戶(hù)體會(huì)到點(diǎn)擊圖標(biāo)之后立馬就有響應(yīng)。
如果你的application或activity啟動(dòng)的過(guò)程太慢,導(dǎo)致系統(tǒng)的BackgroundWindow沒(méi)有及時(shí)被替換,就會(huì)出現(xiàn)啟動(dòng)時(shí)白屏或黑屏的情況(取決于Theme主題是Dark還是Light)。消除啟動(dòng)時(shí)的黑/白屏問(wèn)題,大部分App都采用自己在Theme中設(shè)置背景圖的方式來(lái)解決。
<style name="AppTheme.Launcher"> <item name="android:windowBackground">@drawable/bg</item> </style> <activity android:name=".activity.SplashActivity" android:screenOrientation="portrait" android:theme="@style/AppTheme.Launcher"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
然后在A(yíng)ctivity的onCreate方法,把Activity設(shè)置回原來(lái)的主題
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
}
這么做,只是提高啟動(dòng)的用戶(hù)體驗(yàn)。并不能做到真正的加快啟動(dòng)速度。
總結(jié):?jiǎn)?dòng)速度優(yōu)化也會(huì)涉及到布局優(yōu)化與卡頓優(yōu)化,包括內(nèi)存抖動(dòng)等問(wèn)題。優(yōu)化是一條持續(xù)的道路,很多時(shí)候我們會(huì)發(fā)現(xiàn)通過(guò)各種檢測(cè)手段花費(fèi)了大量的精力去對(duì)代碼進(jìn)行修改得到的優(yōu)化效果可能并不理想。因?yàn)閮?yōu)化就是一點(diǎn)一滴積累下來(lái)的,我們平時(shí)在編碼的過(guò)程中就需要多注意自己的代碼性能。
到此這篇關(guān)于A(yíng)ndroid 分析實(shí)現(xiàn)性能優(yōu)化之啟動(dòng)速度優(yōu)化的文章就介紹到這了,更多相關(guān)Android 性能優(yōu)化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android文字基線(xiàn)Baseline算法的使用講解
- Android Studio使用Profiler來(lái)完成內(nèi)存泄漏的定位
- Android優(yōu)化提升應(yīng)用啟動(dòng)速度及Splash頁(yè)面的設(shè)計(jì)
- Android10 App 啟動(dòng)分析進(jìn)程創(chuàng)建源碼解析
- Android 10 啟動(dòng)Init進(jìn)程解析
- Android開(kāi)發(fā)注解排列組合出啟動(dòng)任務(wù)ksp
- Android基準(zhǔn)配置文件Baseline?Profile方案提升啟動(dòng)速度
相關(guān)文章
一文讀懂Android?Kotlin的數(shù)據(jù)流
這篇文章主要介紹了一文讀懂Android?Kotlin的數(shù)據(jù)流,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-07-07
Android實(shí)現(xiàn)簡(jiǎn)易的計(jì)算器
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡(jiǎn)易的計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-10-10
基于A(yíng)ndroid中獲取資源的id和url方法總結(jié)
下面小編就為大家分享一篇基于A(yíng)ndroid中獲取資源的id和url方法總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-02-02
Android SQLite數(shù)據(jù)庫(kù)增刪改查操作的使用詳解
本篇文章介紹了,在A(yíng)ndroid中SQLite數(shù)據(jù)庫(kù)增刪改查操作的使用詳解。需要的朋友參考下2013-04-04
關(guān)于android連續(xù)點(diǎn)擊出現(xiàn)多個(gè)Activity界面的解決方法
這篇文章主要介紹了關(guān)于android連續(xù)點(diǎn)擊出現(xiàn)多個(gè)Activity界面的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
Android 使用ViewPager實(shí)現(xiàn)圖片左右循環(huán)滑動(dòng)自動(dòng)播放
這篇文章主要介紹了Android 使用ViewPager實(shí)現(xiàn)圖片左右循環(huán)滑動(dòng)自動(dòng)播放的相關(guān)資料,非常不錯(cuò),具有參考解決價(jià)值,需要的朋友可以參考下2016-08-08

