即学即用Android Jetpack - Startup
前言
即学即用 Android Jetpack 系列Blog的目的是通过学习 Android Jetpack
完成一个简单的Demo,本文是即学即用 Android Jetpack 系列Blog的第七篇。
由于 Android Jetpack 在前一时间里更新了新的库 Startup、Hit 和 Page 3,那么《即学即用Android Jetpack》系列文章就需要加入新的内容了。
在接下来的几周里,除了增加新的 Android Jetpack 文章以外,我还会对之前的入门 Android Jetpack 中的项目 【Hoo】 中所使用的旧的 Jetpack 库进行升级。
Jetpack
系列文章🚀:
第一篇:《即学即用Android Jetpack - Navigation》
第二篇:《即学即用Android Jetpack - Data Binding》
第三篇:《即学即用Android Jetpack - ViewModel & LiveData》
第四篇:《即学即用Android Jetpack - Room》
第五篇:《即学即用Android Jetpack - Paging》
第六篇:《即学即用Android Jetpack - WorkManger》
第七篇:《即学即用Android Jetpack - Startup》
目录
目录一、介绍
从库名 StartUp 来看,不难猜出,它是一个跟启动相关的库。
我们先从背景讲起,一些第三方库需要Activity启动之前去进行初始化,比如说我们之前谈过的 WorkManager
和 常见的数据库相关的库,不可能说进入到 Activity
的时候我再去初始化,因为这种初始化可能会比较耗时,给用户带来的体验也比较差。
我们再来谈一下常用的库初始化的方法:
- 自定义
Application
,并在Application#onCreate()
中进行初始化。优点也是它的缺点,需要手动调用,但是能自己控制调用时机。 - 自定义
ContentProvider
,并在ContentProvider#onCreate()
中进行初始化。优点是自动调用,降低开发者的学习成本,缺点是ContentProvider
是一个相对来说比较重的操作,初始化一个ContentProvider
带来的开销比较小,如果大家开发的第三方库都使用这种操作呢?结果可想而知,延长我们 App 的启动时间。
借用郭神《Jetpack新成员,App Startup一篇就懂》里的空 ContentProvider
初始化的时候的开销:
有了上面的基础,我们就可以讲 StartUp
的使用了,StartUp
采用的是第二种方式,它的目的是仅仅是为了使用一个 ConttentProvider
来初始化那些需要初始化的库。
图片来自《AndroidX: App Startup》,不使用 Startup
的时候:
使用 Startup
的时候:
其实我感觉真正的事实并不是这样的,而是:
真实场景Startup
并不能解决已经使用 ContentProvider
进行初始化的第三方库,而对于没有使用 ContentProvider
初始化,并且需要初始化的库,我可以只选择一个 ContentProvider
进行初始化,或者在 Application
中的 onCreate()
方法中进行初始化,还可以省去引入一个库和创建 ContentProvider
的开销。
不过既然谷歌创建了 Startup
,总归是有用处的,学习方式仍然推荐官方文档:
官方文档:https://developer.android.com/topic/libraries/app-startup
二、使用
Startup
的使用方式很简单。
第一步 添加依赖
dependencies {
implementation "androidx.startup:startup-runtime:1.0.0-alpha02"
}
第二步 实现Initializer
实现 Initializer
需要实现其中的两个方法,我们先以 Room
数据库初始化为例:
class RoomInitializer : Initializer<AppDataBase> {
override fun create(context: Context): AppDataBase {
return AppDataBase.getInstance(context)
}
override fun dependencies(): List<Class<out Initializer<*>>> {
return emptyList()
}
}
需要实现两个方法:
-
onCreate
:在这个方法执行需要初始化的动作,比如在上面的RoomInitializer
中的AppDataBase.getInstance(context)
,我会进行数据的初始化,具体的代码我就不放了。 -
dependencies
:比如说我当前库 A 依赖库 B,B 初始化成功以后才能进行 A 的初始化,这个时候我就需要返回包含 B 的Initializer
的Class
的List
。
我们这里再创建一个 PushInitializer
,假设它必须再 Room
初始化完成以后它才能进行初始化:
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)
}
}
/**
* 这是一个伪造的推送类 PushSdk
*/
data class PushSdk(val name:String){
fun registerPush(){
print("Hello,i'm registering push")
}
}
是不是感觉很简单。
这里谈一下我在官方文档遇到的一个坑,我看到官方文档上可以建 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()
}
}
如果你这样写,恭喜你,接下来,你会收到一个这样的闪退:
java.lang.RuntimeException: Unable to get provider androidx.startup.InitializationProvider: androidx.startup.StartupException: androidx.startup.StartupException: java.lang.IllegalStateException: WorkManager is already initialized.
意思是我们的 WorkManager
已经初始化过了,不需要再初始化了。
因为 WorkManagerInitializer
这个类继承了 ContentProvider
,它已经在 ContentProvider#onCreate()
方法中对 WorkManager
进行了初始化,我们再在 Startup
进行初始化就是多此一举。
第三步 在AndroidManifest文件中声明InitializationProvider
毕竟 Startup
采用的是 ContentProvider
的方式进行初始化,所以在 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
标签,需要初始化的 Initializer
都会被声明成 meta-data
,这里有一点可以简化,在上面一步中,我们声明了 PushInitializer
和 RoomInitializer
,PushInitializer
是依赖于 RoomInitializer
的,被依赖的 RoomInitializer
可以不用申明。
第四步 使用懒加载
你可能会提出这样的一个问题,虽然在 provider
标签中声明了 Initializer
,但是因为某些原因,可不可以在 InitializationProvider
初始化的时候不会去进行相关库的初始化,而是在后面的某个时机手动初始化?
这种懒加载的方式在 Startup
中是存在的,方法是在 meta-data
标签中加入 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>
之后在你想进行初始化的地方调用:
AppInitializer.getInstance(context)
.initializeComponent(PushInitializer::class.java)
三、总结
目前看来,Stratup
的并没有特别大的优势,也许是帮我们简化了依赖逻辑?
如果你有更好的见解,欢迎在下方评论留言。
引用文章: