技術(shù)不止章郁,文章有料,加
JiuXinDev
入群志衍,Android 搬磚路上不孤單
前言
即學(xué)即用 Android Jetpack 系列Blog的目的是通過學(xué)習(xí) Android Jetpack
完成一個(gè)簡單的Demo暖庄,本文是即學(xué)即用 Android Jetpack 系列Blog的第七篇。
由于 Android Jetpack 在前一時(shí)間里更新了新的庫 Startup楼肪、Hit 和 Page 3培廓,那么《即學(xué)即用Android Jetpack》系列文章就需要加入新的內(nèi)容了。
在接下來的幾周里春叫,除了增加新的 Android Jetpack 文章以外肩钠,我還會(huì)對之前的入門 Android Jetpack 中的項(xiàng)目 【Hoo】 中所使用的舊的 Jetpack 庫進(jìn)行升級(jí)。
Jetpack
系列文章??:
第一篇:《即學(xué)即用Android Jetpack - Navigation》
第二篇:《即學(xué)即用Android Jetpack - Data Binding》
第三篇:《即學(xué)即用Android Jetpack - ViewModel & LiveData》
第四篇:《即學(xué)即用Android Jetpack - Room》
第五篇:《即學(xué)即用Android Jetpack - Paging》
第六篇:《即學(xué)即用Android Jetpack - WorkManger》
第七篇:《即學(xué)即用Android Jetpack - Startup》
項(xiàng)目總結(jié)篇:《學(xué)習(xí)Android Jetpack? 實(shí)戰(zhàn)和教程這里全都有象缀!》
目錄
一蔬将、介紹
從庫名 StartUp 來看爷速,不難猜出央星,它是一個(gè)跟啟動(dòng)相關(guān)的庫。
我們先從背景講起惫东,一些第三方庫需要Activity啟動(dòng)之前去進(jìn)行初始化莉给,比如說我們之前談過的 WorkManager
和 常見的數(shù)據(jù)庫相關(guān)的庫,不可能說進(jìn)入到 Activity
的時(shí)候我再去初始化廉沮,因?yàn)檫@種初始化可能會(huì)比較耗時(shí)颓遏,給用戶帶來的體驗(yàn)也比較差。
我們再來談一下常用的庫初始化的方法:
- 自定義
Application
滞时,并在Application#onCreate()
中進(jìn)行初始化叁幢。優(yōu)點(diǎn)也是它的缺點(diǎn),需要手動(dòng)調(diào)用坪稽,但是能自己控制調(diào)用時(shí)機(jī)曼玩。 - 自定義
ContentProvider
,并在ContentProvider#onCreate()
中進(jìn)行初始化窒百。優(yōu)點(diǎn)是自動(dòng)調(diào)用黍判,降低開發(fā)者的學(xué)習(xí)成本,缺點(diǎn)是ContentProvider
是一個(gè)相對來說比較重的操作篙梢,初始化一個(gè)ContentProvider
帶來的開銷比較小顷帖,如果大家開發(fā)的第三方庫都使用這種操作呢?結(jié)果可想而知,延長我們 App 的啟動(dòng)時(shí)間贬墩。
借用郭神《Jetpack新成員榴嗅,App Startup一篇就懂》里的空 ContentProvider
初始化的時(shí)候的開銷:
有了上面的基礎(chǔ),我們就可以講 StartUp
的使用了陶舞,StartUp
采用的是第二種方式录肯,它的目的是僅僅是為了使用一個(gè) ConttentProvider
來初始化那些需要初始化的庫。
圖片來自《AndroidX: App Startup》吊说,不使用 Startup
的時(shí)候:
使用 Startup
的時(shí)候:
其實(shí)我感覺真正的事實(shí)并不是這樣的论咏,而是:
Startup
并不能解決已經(jīng)使用 ContentProvider
進(jìn)行初始化的第三方庫,而對于沒有使用 ContentProvider
初始化颁井,并且需要初始化的庫厅贪,我可以只選擇一個(gè) ContentProvider
進(jìn)行初始化,或者在 Application
中的 onCreate()
方法中進(jìn)行初始化雅宾,還可以省去引入一個(gè)庫和創(chuàng)建 ContentProvider
的開銷养涮。
不過既然谷歌創(chuàng)建了 Startup
,總歸是有用處的眉抬,學(xué)習(xí)方式仍然推薦官方文檔:
官方文檔:https://developer.android.com/topic/libraries/app-startup
二贯吓、使用
Startup
的使用方式很簡單。
第一步 添加依賴
dependencies {
implementation "androidx.startup:startup-runtime:1.0.0-alpha02"
}
第二步 實(shí)現(xiàn)Initializer
實(shí)現(xiàn) Initializer
需要實(shí)現(xiàn)其中的兩個(gè)方法蜀变,我們先以 Room
數(shù)據(jù)庫初始化為例:
class RoomInitializer : Initializer<AppDataBase> {
override fun create(context: Context): AppDataBase {
return AppDataBase.getInstance(context)
}
override fun dependencies(): List<Class<out Initializer<*>>> {
return emptyList()
}
}
需要實(shí)現(xiàn)兩個(gè)方法:
-
onCreate
:在這個(gè)方法執(zhí)行需要初始化的動(dòng)作悄谐,比如在上面的RoomInitializer
中的AppDataBase.getInstance(context)
,我會(huì)進(jìn)行數(shù)據(jù)的初始化库北,具體的代碼我就不放了爬舰。 -
dependencies
:比如說我當(dāng)前庫 A 依賴庫 B,B 初始化成功以后才能進(jìn)行 A 的初始化寒瓦,這個(gè)時(shí)候我就需要返回包含 B 的Initializer
的Class
的List
情屹。
我們這里再創(chuàng)建一個(gè) PushInitializer
,假設(shè)它必須再 Room
初始化完成以后它才能進(jìn)行初始化:
class PushInitializer :Initializer<PushSdk>{
override fun create(context: Context): PushSdk {
val push = PushSdk("MPush")
push.registerPush()
return push
}
override fun dependencies(): List<Class<out Initializer<*>>> {
return listOf(RoomInitializer::class.java)
}
}
/**
* 這是一個(gè)偽造的推送類 PushSdk
*/
data class PushSdk(val name:String){
fun registerPush(){
print("Hello,i'm registering push")
}
}
是不是感覺很簡單杂腰。
這里談一下我在官方文檔遇到的一個(gè)坑垃你,我看到官方文檔上可以建 WorkManager 的 Initializer
,代碼是這樣的:
// Initializes WorkManager.
class WorkManagerInitializer : Initializer<WorkManager> {
override fun create(context: Context): WorkManager {
val configuration = Configuration.Builder().build()
WorkManager.initialize(context, configuration)
return WorkManager.getInstance()
}
override fun dependencies(): List<Class<out Initializer<*>>> {
// No dependencies on other libraries.
return emptyList()
}
}
如果你這樣寫喂很,恭喜你惜颇,接下來,你會(huì)收到一個(gè)這樣的閃退:
java.lang.RuntimeException: Unable to get provider androidx.startup.InitializationProvider: androidx.startup.StartupException: androidx.startup.StartupException: java.lang.IllegalStateException: WorkManager is already initialized.
意思是我們的 WorkManager
已經(jīng)初始化過了恤筛,不需要再初始化了官还。
因?yàn)?WorkManagerInitializer
這個(gè)類繼承了 ContentProvider
,它已經(jīng)在 ContentProvider#onCreate()
方法中對 WorkManager
進(jìn)行了初始化毒坛,我們再在 Startup
進(jìn)行初始化就是多此一舉望伦。
第三步 在AndroidManifest文件中聲明InitializationProvider
畢竟 Startup
采用的是 ContentProvider
的方式進(jìn)行初始化林说,所以在 AndroidManifest
初始化是避免不了的。
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="com.joe.jetpackdemo.androidx-startup"
android:exported="false"
tools:node="merge">
<!-- This entry makes ExampleLoggerInitializer discoverable. -->
<meta-data
android:name="com.joe.jetpackdemo.common.start.PushInitializer"
android:value="androidx.startup" />
</provider>
我們需要注意的是 meta-data
標(biāo)簽屯伞,需要初始化的 Initializer
都會(huì)被聲明成 meta-data
腿箩,這里有一點(diǎn)可以簡化,在上面一步中劣摇,我們聲明了 PushInitializer
和 RoomInitializer
珠移,PushInitializer
是依賴于 RoomInitializer
的,被依賴的 RoomInitializer
可以不用申明末融。
第四步 使用懶加載
你可能會(huì)提出這樣的一個(gè)問題钧惧,雖然在 provider
標(biāo)簽中聲明了 Initializer
,但是因?yàn)槟承┰蚬聪埃刹豢梢栽?InitializationProvider
初始化的時(shí)候不會(huì)去進(jìn)行相關(guān)庫的初始化浓瞪,而是在后面的某個(gè)時(shí)機(jī)手動(dòng)初始化?
這種懶加載的方式在 Startup
中是存在的巧婶,方法是在 meta-data
標(biāo)簽中加入 tools:node="remove"
:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="com.joe.jetpackdemo.androidx-startup"
android:exported="false"
tools:node="merge">
<!-- This entry makes ExampleLoggerInitializer discoverable. -->
<meta-data
android:name="com.joe.jetpackdemo.common.start.PushInitializer"
android:value="androidx.startup"
tools:node="remove"/>
</provider>
之后在你想進(jìn)行初始化的地方調(diào)用:
AppInitializer.getInstance(context)
.initializeComponent(PushInitializer::class.java)
三乾颁、總結(jié)
目前看來,Stratup
的并沒有特別大的優(yōu)勢艺栈,也許是幫我們簡化了依賴邏輯英岭?
如果你有更好的見解,歡迎在下方評論留言湿右。
引用文章: