前言
最近Jetpack
又添加了新成員App Startup
,官方聲明這是一個在Android應(yīng)用啟動時筋遭,針對初始化組件進(jìn)行優(yōu)化的依賴庫涤姊。本人第一次聽到后非常高興,因為自己負(fù)責(zé)的項目在啟動時需要初始化的東西實在是太多崩哩,而且有點雜亂無章,都耦合在一起了言沐。對于可以異步初始化的組件也沒有進(jìn)行異步處理邓嘹,而對于已經(jīng)處理過的異步組件它們之間的依賴關(guān)系或者多個異步之后的統(tǒng)一邏輯處理也沒有一個很好的統(tǒng)一規(guī)范。所以針對這種情況早就想找個方案來優(yōu)化了险胰,這次終于等到了App Startup
吴超。
但是,當(dāng)我元氣滿滿的去查看官方文檔時鸯乃,并沒有找到預(yù)想中的結(jié)果。官方文檔中只提到了可以通過一個ContentProvider
來統(tǒng)一管理需要初始化的組件,同時通過dependencies()
方法解決組件間初始化的依賴順序缨睡,然后呢鸟悴?沒了?等等官方你是不是漏了什么奖年?
異步處理呢细诸?雖然我們可以在create()
方法中手動創(chuàng)建子線程進(jìn)行異步任務(wù),但一個異步任務(wù)依賴另一個異步任務(wù)又該如何處理呢陋守?多個異步任務(wù)完成之后震贵,統(tǒng)一邏輯處理又在哪里呢?依賴任務(wù)完成后的回調(diào)又在哪里水评?亦或者是依賴任務(wù)完成后的通知猩系?
我有點不相信,所以又去查看了App Startup
的源碼中燥,源碼很簡單寇甸,也就幾個文件,最后發(fā)現(xiàn)確實只支持上面的那幾個功能疗涉。
如果你的項目都是同步初始化的話拿霉,并且使用到了多個ContentProvider
,App Startup
可能有一定的優(yōu)化空間咱扣,畢竟統(tǒng)一到了一個ContentProvider
中绽淘,同時支持了簡單的順序依賴。
值得一提的是闹伪,App Startup
中只提供了使用反射來獲取初始化的組件實例沪铭,這對于一些沒有過多依賴的初始化項目來說,盲目使用App Startup
來優(yōu)化是否會對啟動速度進(jìn)一步造成影響呢祭往?
所以細(xì)想了一下伦意,不禁讓我想起了三國時的一個名詞:雞肋
。食之無味硼补,棄之可惜驮肉。
但最終我還是決定放棄使用它。
放棄之后有點不甘心已骇,可能更多的是它沒有解決我當(dāng)前的項目場景离钝。都分析了這么多,源碼都看了褪储,總不能半途而廢吧卵渴,所以自己咬咬牙再補(bǔ)充一點唄。
所以堅持一下鲤竹,就有了下面這個庫浪读,App Startup
的進(jìn)階版Android Startup
。
Android Startup
Android Startup提供一種在應(yīng)用啟動時能夠更加簡單、高效的方式來初始化組件碘橘。開發(fā)人員可以使用Android Startup
來簡化啟動序列互订,并顯式地設(shè)置初始化順序與組件之間的依賴關(guān)系。
與此同時痘拆,Android Startup
支持同步與異步等待仰禽,并通過有向無環(huán)圖拓?fù)渑判?/a>的方式來保證內(nèi)部依賴組件的初始化順序。
由于Android Startup
是基于App Startup
進(jìn)行的擴(kuò)展纺蛆,所以它的使用方式與App Startup
有點類似吐葵,該有的功能基本上都有,同時額外還附加其它功能桥氏。
下面是一張與google的App Startup功能對比的表格温峭。
指標(biāo) | App Startup | Android Startup |
---|---|---|
手動配置 | ? | ? |
自動配置 | ? | ? |
依賴支持 | ? | ? |
閉環(huán)處理 | ? | ? |
線程控制 | ? | ? |
異步等待 | ? | ? |
依賴回調(diào) | ? | ? |
拓?fù)鋬?yōu)化 | ? | ? |
下面簡單介紹一下Android Startup
的使用。
添加依賴
將下面的依賴添加到build.gradle
文件中:
dependencies {
implementation 'com.rousetime.android:android-startup:1.0.1'
}
依賴版本的更新信息: Release
快速使用
android-startup提供了兩種使用方式识颊,在使用之前需要先定義初始化的組件诚镰。
定義初始化的組件
每一個初始化的組件都需要實現(xiàn)AndroidStartup<T>
抽象類,它實現(xiàn)了Startup<T>
接口祥款,它主要有以下四個抽象方法:
callCreateOnMainThread(): Boolean
用來控制create()
方法調(diào)時所在的線程清笨,返回true代表在主線程執(zhí)行。waitOnMainThread(): Boolean
用來控制當(dāng)前初始化的組件是否需要在主線程進(jìn)行等待其完成刃跛。如果返回true抠艾,將在主線程等待,并且阻塞主線程。create(): T?
組件初始化方法,執(zhí)行需要處理的初始化邏輯赴肚,支持返回一個T
類型的實例。dependencies(): List<Class<out Startup<*>>>?
返回Startup<*>
類型的list集合齐苛。用來表示當(dāng)前組件在執(zhí)行之前需要依賴的組件。
例如桂塞,下面定義一個SampleFirstStartup
類來實現(xiàn)AndroidStartup<String>
抽象類:
class SampleFirstStartup : AndroidStartup<String>() {
override fun callCreateOnMainThread(): Boolean = true
override fun waitOnMainThread(): Boolean = false
override fun create(context: Context): String? {
// todo something
return this.javaClass.simpleName
}
override fun dependencies(): List<Class<out Startup<*>>>? {
return null
}
}
因為SampleFirstStartup
在執(zhí)行之前不需要依賴其它組件凹蜂,所以它的dependencies()
方法可以返回空,同時它會在主線程中執(zhí)行阁危。
注意:?雖然
waitOnMainThread()
返回了false
玛痊,但由于它是在主線程中執(zhí)行,而主線程默認(rèn)是阻塞的狂打,所以callCreateOnMainThread()
返回true
時擂煞,該方法設(shè)置將失效。
假設(shè)你還需要定義SampleSecondStartup
趴乡,它依賴于SampleFirstStartup
对省。這意味著在執(zhí)行SampleSecondStartup
之前SampleFirstStartup
必須先執(zhí)行完畢蝗拿。
class SampleSecondStartup : AndroidStartup<Boolean>() {
override fun callCreateOnMainThread(): Boolean = false
override fun waitOnMainThread(): Boolean = true
override fun create(context: Context): Boolean {
// 模仿執(zhí)行耗時
Thread.sleep(5000)
return true
}
override fun dependencies(): List<Class<out Startup<*>>>? {
return listOf(SampleFirstStartup::class.java)
}
}
在dependencies()
方法中返回了SampleFirstStartup
,所以它能保證SampleFirstStartup
優(yōu)先執(zhí)行完畢官辽。
它會在子線程中執(zhí)行蛹磺,但由于waitOnMainThread()
返回了true
,所以主線程會阻塞等待直到它執(zhí)行完畢同仆。
例如,你還定義了SampleThirdStartup與SampleFourthStartup
Manifest中自動配置
第一種初始化方法是在Manifest中進(jìn)行自動配置裙品。
在Android Startup中提供了StartupProvider
類俗批,它是一個特殊的content provider,提供自動識別在manifest中配置的初始化組件市怎。
為了讓其能夠自動識別岁忘,需要在StartupProvider
中定義<meta-data>
標(biāo)簽。其中的name
為定義的組件類区匠,value
的值對應(yīng)為android.startup
干像。
<provider
android:name="com.rousetime.android_startup.provider.StartupProvider"
android:authorities="${applicationId}.android_startup"
android:exported="false">
<meta-data
android:name="com.rousetime.sample.startup.SampleFourthStartup"
android:value="android.startup" />
</provider>
你不需要將SampleFirstStartup
、SampleSecondStartup
與SampleThirdStartup
添加到<meta-data>
標(biāo)簽中驰弄。這是因為在SampleFourthStartup
中麻汰,它的dependencies()
中依賴了這些組件。StartupProvider
會自動識別已經(jīng)聲明的組件中依賴的其它組件戚篙。
Application中手動配置
第二種初始化方法是在Application進(jìn)行手動配置五鲫。
手動初始化需要使用到StartupManager.Builder()
。
例如岔擂,如下代碼使用StartupManager.Builder()
進(jìn)行初始化配置位喂。
class SampleApplication : Application() {
override fun onCreate() {
super.onCreate()
StartupManager.Builder()
.addStartup(SampleFirstStartup())
.addStartup(SampleSecondStartup())
.addStartup(SampleThirdStartup())
.addStartup(SampleFourthStartup())
.build(this)
.start()
.await()
}
}
如果你開啟了日志輸出,然后運(yùn)行項目之后乱灵,將會在控制臺中輸出經(jīng)過拓?fù)渑判騼?yōu)化之后的初始化組件的執(zhí)行順序塑崖。
D/StartupTrack: TopologySort result:
================================================ ordering start ================================================
order [0] Class: SampleFirstStartup => Dependencies size: 0 => callCreateOnMainThread: true => waitOnMainThread: false
order [1] Class: SampleSecondStartup => Dependencies size: 1 => callCreateOnMainThread: false => waitOnMainThread: true
order [2] Class: SampleThirdStartup => Dependencies size: 2 => callCreateOnMainThread: false => waitOnMainThread: false
order [3] Class: SampleFourthStartup => Dependencies size: 3 => callCreateOnMainThread: false => waitOnMainThread: false
================================================ ordering end ================================================
完整的代碼實例,你可以通過查看app獲取痛倚。
更多
可選配置
LoggerLevel
: 控制Android Startup中的日志輸出规婆,可選值包括LoggerLevel.NONE
,LoggerLevel.ERROR
andLoggerLevel.DEBUG
。AwaitTimeout
: 控制Android Startup中主線程的超時等待時間状原,即阻塞的最長時間聋呢。
Manifest中配置
使用這些配置,你需要定義一個類去實現(xiàn)StartupProviderConfig
接口颠区,并且實現(xiàn)它的對應(yīng)方法削锰。
class SampleStartupProviderConfig : StartupProviderConfig {
override fun getConfig(): StartupConfig =
StartupConfig.Builder()
.setLoggerLevel(LoggerLevel.DEBUG)
.setAwaitTimeout(12000L)
.build()
}
與此同時,你還需要在manifest中進(jìn)行配置StartupProviderConfig
毕莱。
<provider
android:name="com.rousetime.android_startup.provider.StartupProvider"
android:authorities="${applicationId}.android_startup"
android:exported="false">
<meta-data
android:name="com.rousetime.sample.startup.SampleStartupProviderConfig"
android:value="android.startup.provider.config" />
</provider>
經(jīng)過上面的配置器贩,StartupProvider
會自動解析SampleStartupProviderConfig
颅夺。
Application中配置
在Application需要借助StartupManager.Builder()
進(jìn)行配置。
override fun onCreate() {
super.onCreate()
val config = StartupConfig.Builder()
.setLoggerLevel(LoggerLevel.DEBUG)
.setAwaitTimeout(12000L)
.build()
StartupManager.Builder()
.setConfig(config)
...
.build(this)
.start()
.await()
}
方法
AndroidStartup
createExecutor(): Executor
: 如果定義的組件沒有運(yùn)行在主線程蛹稍,那么可以通過該方法進(jìn)行控制運(yùn)行的子線程吧黄。onDependenciesCompleted(startup: Startup<*>, result: Any?)
: 該方法會在每一個依賴執(zhí)行完畢之后進(jìn)行回調(diào)。
實戰(zhàn)測試
AwesomeGithub中使用了Android Startup唆姐,優(yōu)化配置的初始化時間與組件化開發(fā)的配置注入時機(jī)拗慨,使用前與使用后時間對比:
狀態(tài) | 啟動頁面 | 消耗時間 |
---|---|---|
使用前 | WelcomeActivity | 420ms |
使用后 | WelcomeActivity | 333ms |
AwesomeGithub
AwesomeGithub是基于Github的客戶端,純練習(xí)項目奉芦,支持組件化開發(fā)赵抢,支持賬戶密碼與認(rèn)證登陸。使用Kotlin語言進(jìn)行開發(fā)声功,項目架構(gòu)是基于JetPack&DataBinding的MVVM烦却;項目中使用了Arouter、Retrofit先巴、Coroutine其爵、Glide、Dagger與Hilt等流行開源技術(shù)伸蚯。
除了Android原生版本摩渺,還有基于Flutter的跨平臺版本flutter_github。
如果你喜歡我的文章朝卒,你可以關(guān)注我的微信公眾號:【Android補(bǔ)給站】或者掃描下方二維碼進(jìn)行關(guān)注证逻,當(dāng)然你也可以直接關(guān)注當(dāng)前網(wǎng)站的帳號。主要區(qū)別就是微信能夠更方法互動抗斤。
公眾號更新不會很頻繁囚企,但一旦更新必定是純干貨。