android啟動(dòng)優(yōu)化解析

性能優(yōu)化的方案不少,但能落地應(yīng)用到項(xiàng)目中的卻不多

前段時(shí)間學(xué)習(xí)布局優(yōu)化時(shí),總結(jié)了不少布局優(yōu)化的方案,但最后卻無(wú)法在項(xiàng)目中應(yīng)用:【從入門(mén)到放棄】android布局優(yōu)化深入解析

今天一如既往,不做標(biāo)題黨,給大家介紹一些可以實(shí)用的啟動(dòng)優(yōu)化方案~

對(duì)于android APP來(lái)說(shuō),啟動(dòng)時(shí)間是用戶(hù)的第一體驗(yàn),如果啟動(dòng)時(shí)間過(guò)長(zhǎng),很可能會(huì)流失用戶(hù),所以啟動(dòng)優(yōu)化也是我們做性能優(yōu)化的一個(gè)重要方向

本文主要包括以下內(nèi)容

1.啟動(dòng)優(yōu)化有哪些優(yōu)化方向?

2.如何精準(zhǔn)測(cè)量啟動(dòng)時(shí)間?

3.有哪些實(shí)用的優(yōu)化手段?

啟動(dòng)優(yōu)化有哪些優(yōu)化方向?

應(yīng)用有三種啟動(dòng)狀態(tài),每種狀態(tài)都會(huì)影響應(yīng)用向用戶(hù)顯示所需的時(shí)間:冷啟動(dòng)、溫啟動(dòng)或熱啟動(dòng)。在冷啟動(dòng)中,應(yīng)用從頭開(kāi)始啟動(dòng)。在另外兩種狀態(tài)中,系統(tǒng)需要將后臺(tái)運(yùn)行的應(yīng)用帶入前臺(tái)。

本文所說(shuō)的啟動(dòng)優(yōu)化都是指冷啟動(dòng)優(yōu)化

要優(yōu)化應(yīng)用以實(shí)現(xiàn)快速啟動(dòng),了解系統(tǒng)和應(yīng)用層面的情況以及它們?cè)诟鱾€(gè)狀態(tài)中的互動(dòng)方式很有幫助。????

冷啟動(dòng)

冷啟動(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)時(shí)間帶來(lái)了最大的挑戰(zhàn),因?yàn)橄到y(tǒng)和應(yīng)用要做的工作比在另外兩種啟動(dòng)狀態(tài)中更多。

在冷啟動(dòng)開(kāi)始時(shí),系統(tǒng)有三個(gè)任務(wù),它們是:

1.加載并啟動(dòng)應(yīng)用。

2.在啟動(dòng)后立即顯示應(yīng)用的空白啟動(dòng)窗口。

3.創(chuàng)建應(yīng)用進(jìn)程。

系統(tǒng)一創(chuàng)建應(yīng)用進(jìn)程,應(yīng)用進(jìn)程就負(fù)責(zé)后續(xù)階段:

1.創(chuàng)建應(yīng)用對(duì)象。

2.啟動(dòng)主線(xiàn)程。

3.創(chuàng)建主Activity。

4.填充視圖。

5.布局屏幕。

6.執(zhí)行初始繪制。

一旦應(yīng)用進(jìn)程完成第一次繪制,系統(tǒng)進(jìn)程就會(huì)換掉當(dāng)前顯示的后臺(tái)窗口,替換為主Activity。此時(shí),用戶(hù)可以開(kāi)始使用應(yīng)用。

如上所示,Application與Activity的生命周期就是我們的優(yōu)化方向

一般為Application onCreate方法與首個(gè)Activity加載耗時(shí)

如何測(cè)量啟動(dòng)時(shí)間?

最簡(jiǎn)單方法

通過(guò)查看logcat可以快速了解啟動(dòng)時(shí)間

在Android Studio Logcat中過(guò)濾關(guān)鍵字Displayed,可以看到對(duì)應(yīng)的冷啟動(dòng)耗時(shí)日志。

命令測(cè)量

for i in `seq 1 10`do? adb shell am force-stop com.xx.xx? sleep 2? adb shell am start-activity -W -n 包名/activity名稱(chēng) | grep "TotalTime" | cut -d ' ' -f 2done復(fù)制代碼

有時(shí)候我們需要統(tǒng)計(jì)app的冷啟動(dòng)性能,單次結(jié)果往往不準(zhǔn)確 還需要多次統(tǒng)計(jì)以后做平均值

如上,使用腳本啟動(dòng)10次首頁(yè)Activity,可以比較準(zhǔn)確的獲取冷啟動(dòng)性能

命令測(cè)量方式線(xiàn)下使用方便,可以測(cè)試競(jìng)品

但是不能帶到線(xiàn)上,不能精準(zhǔn)控制測(cè)量時(shí)間

所以我們通常還需要手動(dòng)埋點(diǎn)

埋點(diǎn)測(cè)量

埋點(diǎn)測(cè)量關(guān)鍵點(diǎn)在于合適的開(kāi)始與結(jié)束時(shí)間

我們一般使用Application attachBaseContext作為開(kāi)始時(shí)間

而啟動(dòng)結(jié)束時(shí)間則有很多選擇

IdleHandler

IdleHandler在MessageQueue空閑時(shí)會(huì)回調(diào),也就是所在線(xiàn)程任務(wù)已經(jīng)執(zhí)行完時(shí),線(xiàn)程處于空閑狀態(tài)時(shí)才會(huì)執(zhí)行Idle列表中的任務(wù)。

正常情況下,當(dāng)主線(xiàn)程程處于空閑狀態(tài)時(shí),可以認(rèn)為冷啟動(dòng)已經(jīng)完成,是一個(gè)比較不錯(cuò)的打點(diǎn)時(shí)機(jī)

但有個(gè)問(wèn)題,如果UI線(xiàn)程的任務(wù)一直不執(zhí)行完呢?如果有其他任務(wù)加入了MessageQueue但是頁(yè)面已經(jīng)可見(jiàn)了呢?

IdleHandler具有一定的不可控特性,讀者可根據(jù)項(xiàng)目特性判斷是否使用

onWindowFocusChanged

當(dāng)Activity回調(diào)onWindowFocusChanged時(shí),我們可以認(rèn)為Activity已經(jīng)可見(jiàn)了,所以可以在此時(shí)打點(diǎn)

不過(guò)onWindowFocusChanged方法只是Activity的首幀時(shí)間,是Activity首次進(jìn)行繪制的時(shí)間,首幀時(shí)間和界面完整展示出來(lái)還有一段時(shí)間差,不能真正代表界面已經(jīng)展現(xiàn)出來(lái)了。

但是onWindowFocusChanged方式與業(yè)務(wù)耦合少,侵入性低,使用起來(lái)比較方便

在我們的項(xiàng)目中回調(diào)時(shí)間與界面展示時(shí)間相差很少,可以作為一種可選方案,根據(jù)實(shí)際情況使用

onPrewDrawListener

如上面所說(shuō),正確的計(jì)算啟動(dòng)耗時(shí)的時(shí)機(jī)是要等真實(shí)的數(shù)據(jù)展示出來(lái),比如在列表第一項(xiàng)的展示時(shí)再計(jì)算啟動(dòng)耗時(shí)。

我們可以在給列表的第一項(xiàng)添加onPreDrawListener監(jiān)聽(tīng),這種方式比較準(zhǔn)確

不過(guò)這種方式與業(yè)務(wù)代碼強(qiáng)相關(guān),侵入性較大。讀者也可根據(jù)實(shí)際情況使用

AOP測(cè)量方法耗時(shí)

我們的Application中初始化了很多第三方庫(kù),有時(shí)我們需要統(tǒng)計(jì)每個(gè)方法的耗時(shí),確定是哪個(gè)方法比較耗時(shí),如果一個(gè)一個(gè)添加是很麻煩的

這種時(shí)候就可以使用AOP面向切面編程

如果想了解詳情,請(qǐng)參閱使用AOP測(cè)量方法耗時(shí)

TraceView與SystraceView使用

TraceView

TraceView可以跟蹤App某段時(shí)間內(nèi)所有調(diào)用過(guò)的方法,這是測(cè)量應(yīng)用執(zhí)行性能常用的方式之一.

通過(guò)它我們可以查出App啟動(dòng)時(shí)具體都調(diào)用了方法,都花了多長(zhǎng)時(shí)間。

這個(gè)功能是Android系統(tǒng)提供的,我們可以通過(guò)在代碼里手動(dòng)調(diào)用android.os.Debug.startMethodTracing()和stopMethodTracing()方法來(lái)開(kāi)始和結(jié)束Tracing,然后系統(tǒng)會(huì)把Tracing的結(jié)果保存到手機(jī)的.trace文件里。

此外,除了通過(guò)寫(xiě)代碼來(lái)Trace,我們也有更方便的方式。例如也可以通過(guò)Android Studio Profiler里的Method Tracer來(lái)Trace。但是,針對(duì)App的冷啟動(dòng),我們則通常會(huì)用Android系統(tǒng)自帶的Am命令來(lái)跟蹤,因?yàn)樗軠?zhǔn)確的在App啟動(dòng)的時(shí)候就開(kāi)始Trace:

# 啟動(dòng)指定 Activity,并同時(shí)進(jìn)行采樣跟蹤,-P在app進(jìn)入idle狀態(tài)時(shí)profiler結(jié)束adb shell am start -n com.xxx.android/com.xxx.android.app.ui.activity.MainActivity -P /data/local/tmp/xxx-startup.trace --sampling 1000# 拉取 .trace 文件到本機(jī)當(dāng)前目錄adb pull /data/local/tmp/xx-startup.trace .復(fù)制代碼

如上所示:-P參數(shù)表示在app進(jìn)入idle狀態(tài)時(shí)會(huì)自動(dòng)結(jié)束,這樣就不必手動(dòng)打點(diǎn)了

在啟動(dòng)結(jié)束后,通過(guò)adb pull拉取trace文件后,直接拖到android studio中打開(kāi)就可以查找耗時(shí)方法了

更多關(guān)于TraceView定位啟動(dòng)優(yōu)化問(wèn)題的實(shí)例,讀者可參閱:

TraceView使用

知乎 Android 客戶(hù)端啟動(dòng)優(yōu)化 - Retrofit 代理

Systrace

TraceView雖然是找出耗時(shí)方法的利器,但是執(zhí)行TraceView時(shí)的運(yùn)行環(huán)境和用戶(hù)最終運(yùn)行的環(huán)境會(huì)有極大的差距,因?yàn)門(mén)raceView會(huì)嚴(yán)重拖慢App的執(zhí)行速度。

即使使用采樣跟蹤,測(cè)量得到的結(jié)果和實(shí)際結(jié)果肯定還是有很大偏差,只能作為參考。

而且TraceView更偏向于追查應(yīng)用的內(nèi)因,對(duì)于運(yùn)行環(huán)境等外因(鎖、GC、資源匱乏等)的追查顯得很無(wú)力。

所以,我們可以借助另一個(gè)Google官方極力推薦的工具 - 「Systrace」來(lái)跟蹤 App 實(shí)際運(yùn)行時(shí)的情況

運(yùn)行app后,手動(dòng)殺掉。然后cd到SDK目錄下的platform-tools/systrace下,使用命令:

python systrace.py -t 10 -o /Users/xxx/trace.html -a com.xx.xxx復(fù)制代碼

其中:-t10是指跟蹤10秒,-o表示把文件輸出到指定目錄下,-a是指定應(yīng)用包名。

輸入完這行命令后,可以看到開(kāi)始跟蹤的提示。看到Starting tracing 后,手動(dòng)打開(kāi)我們的應(yīng)用。

等到運(yùn)行結(jié)束后打開(kāi)輸出的trace.html


除了以上外,我們也可以通過(guò)TraceCompact.beginSection來(lái)指定關(guān)注的時(shí)間段

更多關(guān)于Systrace使用的實(shí)例,讀者可以參閱:

systrace使用

知乎 Android 客戶(hù)端啟動(dòng)優(yōu)化 - Retrofit 代理-Systrace

小結(jié)

1.TraceView可以用來(lái)定位具體耗時(shí)的方法

2.TraceView運(yùn)行時(shí)開(kāi)銷(xiāo)嚴(yán)重,導(dǎo)致整體性能變慢,可能會(huì)帶偏優(yōu)化方向

3.Systrace開(kāi)銷(xiāo)小,可以直觀反映Cpu使用率,便于查找運(yùn)行環(huán)境等外因(鎖,GC)等引起的問(wèn)題

4.TraceView與Systrace都可以埋點(diǎn),指定關(guān)心的區(qū)域

常規(guī)優(yōu)化手段

1.Theme切換

Theme設(shè)置可以說(shuō)是啟動(dòng)優(yōu)化的一個(gè)必備手段了

啟動(dòng)Activity的windowBackground主題屬性預(yù)先設(shè)置一個(gè)啟動(dòng)圖片(layer-list實(shí)現(xiàn)),在啟動(dòng)后,在Activity的onCreate()方法中的super.onCreate()前再setTheme(R.style.AppTheme)。

優(yōu)點(diǎn)

1.使用簡(jiǎn)單。

2.避免了啟動(dòng)白屏和點(diǎn)擊啟動(dòng)圖標(biāo)不響應(yīng)的情況。

缺點(diǎn)

治標(biāo)不治本,表面上產(chǎn)生一種快的感覺(jué)。

2.異步方案

我們通常會(huì)在Application的onCreate中初始化很多任務(wù),比如第三方庫(kù)初始化,而且是串行的

這些初始化任務(wù)的耗時(shí)通常還不小,所以一個(gè)優(yōu)化思路就是并行的初始化

這樣就將初始化耗時(shí)從加法變成了求最大值

核心思路:子線(xiàn)程分擔(dān)主線(xiàn)程任務(wù),并行減少時(shí)間

常規(guī)異步方案的問(wèn)題

1.代碼不夠優(yōu)雅

假如我們有 100 個(gè)初始化任務(wù),那我們就需要提交 100 次任務(wù)。

2.無(wú)法限制在onCreate中完成

有的第三方庫(kù)的初始化任務(wù)需要在Application的onCreate方法中執(zhí)行完成,雖然可以用CountDownLatch實(shí)現(xiàn)等待,但是還是有點(diǎn)繁瑣。

3.無(wú)法實(shí)現(xiàn)存在依賴(lài)關(guān)系

有的初始化任務(wù)之間存在依賴(lài)關(guān)系,比如極光推送需要設(shè)備ID,而initDeviceId()這個(gè)方法也是一個(gè)初始化任務(wù)。

異步啟動(dòng)器方案

上面介紹了常規(guī)異步方案的幾個(gè)問(wèn)題,我們可以通過(guò)啟動(dòng)器來(lái)解決

啟動(dòng)器的核心思想是充分利用多核CPU,自動(dòng)梳理任務(wù)順序。

1.第一步是我們要對(duì)代碼進(jìn)行任務(wù)化,任務(wù)化是一個(gè)簡(jiǎn)稱(chēng),比如把啟動(dòng)邏輯抽象成一個(gè)任務(wù)。

2.第二步是根據(jù)所有任務(wù)的依賴(lài)關(guān)系排序生成一個(gè)有向無(wú)環(huán)圖,這個(gè)圖是自動(dòng)生成的,也就是對(duì)所有任務(wù)進(jìn)行排序。

比如我們有個(gè)任務(wù)A和任務(wù)B,任務(wù)B執(zhí)行前需要任務(wù)A執(zhí)行完,這樣才能拿到特定的數(shù)據(jù),比如上面提到的initDeviceId。

3.第三步是多線(xiàn)程根據(jù)排序后的優(yōu)先級(jí)依次執(zhí)行,比如我們現(xiàn)在有三個(gè)任務(wù)A、B、C。假如任務(wù)B依賴(lài)于任務(wù)A,這時(shí)候生成的有向無(wú)環(huán)圖就是ACB,A和C可以提前執(zhí)行,B一定要排在A之后執(zhí)行。


啟動(dòng)器的大致流程如上所示,我們下面介紹幾種開(kāi)源的啟動(dòng)器方案,供讀者參考

JetPack App Startup

1.App Startup這個(gè)庫(kù)提供了一個(gè)組件,可以在應(yīng)用程序啟動(dòng)的時(shí)候初始化。

2.開(kāi)發(fā)人員可以使用這個(gè)組件精簡(jiǎn)啟動(dòng)序列和顯式地設(shè)置初始化的順序。

3.我們不需要為每個(gè)組件定義單獨(dú)的ContentProvider,App Startup允許您定義的所有組件化共享一個(gè)內(nèi)容提供者。

這樣可以極大地減少高應(yīng)用程序的啟動(dòng)時(shí)間,但是App Startup只是支持將多個(gè)ContentProvider合并到一個(gè)ContentProvider中,并指定一定依賴(lài)順序

它的推出的目的,是管理第三方庫(kù)使用ContentProvider過(guò)多,導(dǎo)致啟動(dòng)速度變慢的問(wèn)題

不支持異步與異步任務(wù)管理,所以并不符合我們的要求

阿里-alpha

Alpha是一個(gè)基于PERT圖構(gòu)建的Android異步啟動(dòng)框架,它簡(jiǎn)單,高效,功能完善。 在應(yīng)用啟動(dòng)的時(shí)候,我們通常會(huì)有很多工作需要做,為了提高啟動(dòng)速度,我們會(huì)盡可能讓這些工作并發(fā)進(jìn)行。但這些工作之間可能存在前后依賴(lài)的關(guān)系,所以我們又需要想辦法保證他們執(zhí)行順序的正確性。Alpha就是為此而設(shè)計(jì)的,使用者只需定義好自己的task,并描述它依賴(lài)的task,將它添加到Project中??蚣軙?huì)自動(dòng)并發(fā)有序地執(zhí)行這些task,并將執(zhí)行的結(jié)果拋出來(lái)。 由于Android應(yīng)用支持多進(jìn)程,所以Alpha支持為不同進(jìn)程配置不同的啟動(dòng)模式。

alpha已經(jīng)基本滿(mǎn)足我們的使用,不過(guò)它不支持任務(wù)是否需要等待,同時(shí)它的代碼比較舊,感覺(jué)已經(jīng)很久不維護(hù)了,所以最后決定使用AnchorTask框架

AnchorTask

AnchorTask與Alpha類(lèi)似

1.支持多任務(wù)并發(fā)執(zhí)行

2.支持任務(wù)間依賴(lài)與拓?fù)渑判?/p>

3.支持任務(wù)監(jiān)聽(tīng)與耗時(shí)統(tǒng)計(jì)

4.支持指定任務(wù)優(yōu)先級(jí)

5.支持指定是否在主線(xiàn)程運(yùn)行與是否等待

最主要的一點(diǎn)在于AnchorTask文檔比較強(qiáng)大,從數(shù)據(jù)結(jié)構(gòu)到拓?fù)渑判颍皆O(shè)計(jì)到詳細(xì)的說(shuō)清楚了,有一系列文章,這也是我最后決定使用它的原因

AnchorTaskby程序員徐公

簡(jiǎn)單使用如下,可以通過(guò)鏈?zhǔn)秸{(diào)用靈活的配置任務(wù)與依賴(lài):

valproject =? AnchorProject.Builder().setContext(context).setLogLevel(LogUtils.LogLevel.DEBUG)? ? ? ? ? ? ? ? .setAnchorTaskCreator(ApplicationAnchorTaskCreator())? ? ? ? ? ? ? ? .addTask(TASK_NAME_ZERO)? ? ? ? ? ? ? ? .addTask(TASK_NAME_ONE)? ? ? ? ? ? ? ? .addTask(TASK_NAME_TWO)? ? ? ? ? ? ? ? .addTask(TASK_NAME_THREE).afterTask(? ? ? ? ? ? ? ? ? ? TASK_NAME_ZERO,? ? ? ? ? ? ? ? ? ? TASK_NAME_ONE? ? ? ? ? ? ? ? )? ? ? ? ? ? ? ? .addTask(TASK_NAME_FOUR).afterTask(? ? ? ? ? ? ? ? ? ? TASK_NAME_ONE,? ? ? ? ? ? ? ? ? ? TASK_NAME_TWO? ? ? ? ? ? ? ? )? ? ? ? ? ? ? ? .addTask(TASK_NAME_FIVE).afterTask(? ? ? ? ? ? ? ? ? ? TASK_NAME_THREE,? ? ? ? ? ? ? ? ? ? TASK_NAME_FOUR? ? ? ? ? ? ? ? )? ? ? ? ? ? ? ? .setThreadPoolExecutor(TaskExecutorManager.instance.cpuThreadPoolExecutor)? ? ? ? ? ? ? ? .build()project.start().await()復(fù)制代碼

延遲初始化方案

常規(guī)方案

有些任務(wù)我們需要延遲加載,常規(guī)方法是通過(guò)Handler.postDelayed方法發(fā)送一個(gè)延遲消息,比如延遲到 100 毫秒后執(zhí)行。

常規(guī)方案的問(wèn)題

這種方法有以下幾個(gè)問(wèn)題

1.時(shí)機(jī)不便控制,無(wú)法確定一個(gè)合適的延遲時(shí)間

2.代碼不夠優(yōu)雅,維護(hù)成本高,如果有多個(gè)任務(wù),需要添加多次

3.可能造成主線(xiàn)程卡頓,假如把任務(wù)延遲 200 毫秒后執(zhí)行,而 200 后用戶(hù)還在滑動(dòng)列表,那還是會(huì)發(fā)生卡頓。

更優(yōu)方案

核心思想:對(duì)延遲任務(wù)進(jìn)行分批初始化利用IdleHandler在當(dāng)前消息隊(duì)列空閑時(shí)執(zhí)行的特性,實(shí)現(xiàn)一個(gè)延遲啟動(dòng)器

IdleHandler在返回true時(shí)會(huì)繼續(xù)監(jiān)聽(tīng),返回false結(jié)束監(jiān)聽(tīng)

因此在任務(wù)全部完成后返回false即可,實(shí)現(xiàn)如下:

publicclassDelayInitDispatcher{privateQueue mDelayTasks =newLinkedList<>();privateMessageQueue.IdleHandler mIdleHandler =newMessageQueue.IdleHandler() {@OverridepublicbooleanqueueIdle(){if(mDelayTasks.size()>0){? ? ? ? ? ? ? ? Task task = mDelayTasks.poll();newDispatchRunnable(task).run();? ? ? ? ? ? }return!mDelayTasks.isEmpty();? ? ? ? }? ? };publicDelayInitDispatcheraddTask(Task task){? ? ? ? mDelayTasks.add(task);returnthis;? ? }publicvoidstart(){? ? ? ? Looper.myQueue().addIdleHandler(mIdleHandler);? ? }}//調(diào)用DelayInitDispatcher delayInitDispatcher =newDelayInitDispatcher();delayInitDispatcher.addTask(newDelayInitTaskA())? ? ? ? .addTask(newDelayInitTaskB())? ? ? ? .start();復(fù)制代碼

極致懶加載與提前加載

首頁(yè)極致懶加載

我們的首頁(yè)通常有多個(gè)tab,而當(dāng)我們啟動(dòng)時(shí),只需要初始化一個(gè)tab即可

我們通常會(huì)利用ViewPager來(lái)實(shí)現(xiàn)簡(jiǎn)單的懶加載,比如只有當(dāng)Fragment可見(jiàn)時(shí)才去進(jìn)行網(wǎng)絡(luò)請(qǐng)示

這樣有一定效果,但是View的inflate,measure,layout也需要一定時(shí)間

更加極致的懶加載方案如下:

1.首屏加載時(shí),只往ViewPager中塞入默認(rèn)要展示的tab,剩余的tab用空的占位Fragment代替

2.占位Fragment中只有一個(gè)空白的FrameLayout

3.當(dāng)占位Fragment可見(jiàn)時(shí),將真正要展示的Fragment添加到空白FrameLayout,進(jìn)行真正的初始化

通過(guò)這種方案,可以做到在啟動(dòng)時(shí),只inflate,measure,layout首頁(yè)Fragment的View,其他Tab只有可見(jiàn)時(shí)才會(huì)填充

如果你的Layout比較復(fù)雜的話(huà),通過(guò)這種方式可以較大的改善啟動(dòng)性能

布局預(yù)加載

官方提供了一個(gè)類(lèi),可以來(lái)進(jìn)行異步的inflate,但是有兩個(gè)缺點(diǎn):

1.每次都要現(xiàn)場(chǎng)new一個(gè)出來(lái)

2.異步加載的view只能通過(guò)callback回調(diào)才能獲得,使用不方便(死穴)

3.如果在Activity中進(jìn)行初始化,通過(guò)callback回調(diào)時(shí),并沒(méi)有減少加載時(shí)間,仍然需要等待

由于以上問(wèn)題,一個(gè)思考方向就是,能不能提前在子線(xiàn)程inflate布局,然后在Activity中通過(guò)id取出來(lái)

核心思想如下

1.初始化時(shí)在子線(xiàn)程中inflate布局,存儲(chǔ)在緩存中

2.Activity初始化時(shí),先從緩存結(jié)果里面拿View,拿到了view直接返回

3.沒(méi)拿到view,但是子線(xiàn)程在inflate中,等待返回

4.如果還沒(méi)開(kāi)始inflate,由UI線(xiàn)程進(jìn)行inflate

這種方案的優(yōu)點(diǎn):

可以大大減少View創(chuàng)建的時(shí)間,使用這種方案之后,獲取View的時(shí)候基本在 10ms 之內(nèi)的。

缺點(diǎn)

1.由于View是提前創(chuàng)建的,并且會(huì)存在在一個(gè)map,需要根據(jù)自己的業(yè)務(wù)場(chǎng)景將View從map中移除,不然會(huì)發(fā)生內(nèi)存泄露

2.View如果緩存起來(lái),記得在合適的時(shí)候重置view的狀態(tài),不然有時(shí)候會(huì)發(fā)生奇奇怪怪的現(xiàn)象。

總得來(lái)說(shuō),優(yōu)缺點(diǎn)都很明顯,讀者可根據(jù)實(shí)際情況(主要是項(xiàng)目中inflate的時(shí)間長(zhǎng)不長(zhǎng),改用提前加載后收益明不明顯?),根據(jù)實(shí)際情況決定是否使用

具體實(shí)現(xiàn)可參閱:神奇的的預(yù)加載(預(yù)加載View,而不是data)

總結(jié)

本文主要總結(jié)了啟動(dòng)優(yōu)化的方向,與精準(zhǔn)測(cè)量啟動(dòng)時(shí)間的方式

重點(diǎn)講解了幾種可以實(shí)用的啟動(dòng)優(yōu)化方案:

1.異步啟動(dòng)器加快初始化速度

2.延遲加載器減少卡頓,代碼更加優(yōu)雅

3.首頁(yè)極致懶加載,減少首頁(yè)inflate,measure,layout時(shí)間

4.布局預(yù)加載方案大大減少View創(chuàng)建時(shí)間,讀者可根據(jù)實(shí)際情況使用

這些方案都比較實(shí)用,讀者可以嘗試在項(xiàng)目中應(yīng)用,看看有多少提升~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容