关于Hilt的使用,目前已经比较普及了,想必大家已经知道。今天说的是一个如何利用Hilt来做一个启动框架的故事。
是否经历过大型项目的启动优化,一遍过去无任何效果,第二遍过去好几处报错,第三遍过去启动不了,第四遍过去回滚了代码 这都是为什么呢?
不要怀疑,不要询问,不是你技术菜,也不是逻辑有问题(当然没问题,有问题就不叫优化了,叫改bug),而是启动代码写的乱。
在我们软件行业中,或者说每个人的人生中,乱 这一个字,让多少人悲痛欲绝,让多少人从成功走向了落寞,咳咳,让多少软件行业增本、让多少软件行业无利。
当你打开Application 他的代码量是这样的:
你是不是很崩溃,我们做启动优化的手法一般是,将能并发的并发,能延后的延后, 总之就是要快。但是面对这种代码,真的不敢动。
正文
这种启动优化的方式理念是非常好的,今天我要做的操作是,解决乱代码,将初始化分开做,在这个过程中,我发现可以利用配置引入协程,或有序、或并发、或依赖、或想怎样就怎样,简直爽到不行
如需完整版性能优化学习文档 请点击领取
利用Hilt 搭建启动框架
- 首先,使用接口约束初始化框架
// 后续都需要依赖此框架
interface AppInitializer {
fun init()
}
复制代码
- 其次,将实现了此接口的初始化器,分开并连起来
class AppInitializers @Inject constructor(
private val application: Application,
) {
private val initializers: Set<AppInitializer> by lazy {
EntryPointAccessors.fromApplication(application, AppInitializerEntryPoint::class.java)
.getAppInitializers()
}
fun init() {
for (initializer in initializers) {
initializer.init()
}
}
}
提供一个初始化入口,此代码将在APPlication 中调用,我们使用Hilt注解完成Set 的收集,并且在Application中调用init时 启动初始化,注意 这个地方(init)可以将Set 变为Map,指定策略,实现“或有序、或并发、或依赖、或想怎样就怎样,简直爽到不行”
- 最后,使用
Application 中调用
initializers.init()
Hilt 部分代码
@EntryPoint
@InstallIn(SingletonComponent::class)
interface AppInitializerEntryPoint {
fun getAppInitializers(): Set<AppInitializer>
}
将所初始化器,分别注册到启动器中
@Module
@InstallIn(SingletonComponent::class)
object AppInitializersModule {
@Provides
fun provideAppInitializers(application: Application): Set<AppInitializer> {
return setOf(
EmojiInitializer(application),
UtilsInitializers(application),
)
}
}
这个代码比较简单,这就完事了,将乱代码直接分离开了
制定策略
乱代码是分开了,但是其实用性只在编码层面,想要达到“或有序、或并发、或依赖、或想怎样就怎样,简直爽到不行”这种境界,还需要添加策略
修改注册器
fun getStrategyAppInitializers(): Map<AppInitializer.AppInitializerStrategy, List<AppInitializers>>
我们提供一个策略类
enum class AppInitializerStrategy {
SERIAL,
PARALLEL,
}
添加到总注册器:
@Provides
fun provideStrategyAppInitializers(application: Application): Map<AppInitializer.AppInitializerStrategy, List<AppInitializer>> {
return mapOf(
AppInitializer.AppInitializerStrategy.SERIAL to arrayListOf(EmojiInitializer(application)),
AppInitializer.AppInitializerStrategy.PARALLEL to arrayListOf(UtilsInitializers(application)),
)
}
然后在总的启动器中根据不同的策略配合协程一起使用:
strategyInitializers[AppInitializer.AppInitializerStrategy.SERIAL]?.forEach {
it.init()
}
strategyInitializers[AppInitializer.AppInitializerStrategy.PARALLEL]?.forEach {
MainScope().launch(Dispatchers.IO) {
it.init()
}
}
类图如下:
以下类和接口:
Application
:Android 应用程序类。AppInitializer
接口:定义了一个init()
方法,该方法将在应用程序启动时调用,用于执行一些初始化任务。EmojiInitializer
类:实现了AppInitializer
接口,用于初始化表情符号相关的内容UtilsInitializers
类:实现了AppInitializer
接口,用于初始化一些实用工具。AppInitializerEntryPoint
接口:定义了获取应用程序初始化器的方法,以及获取不同策略的应用程序初始化器列表的方法。AppInitializersModule
类:使用 Dagger2 提供AppInitializerEntryPoint
接口的实例。AppInitializerStrategy
枚举类:定义了应用程序初始化器的两种不同的策略:串行和并行。
以下关系:
-
Application
类与AppInitializerEntryPoint
接口之间的关系:Application
类使用AppInitializerEntryPoint
接口来获取应用程序初始化器。 -
AppInitializerEntryPoint
接口与AppInitializer
接口之间的关系:AppInitializerEntryPoint
接口使用AppInitializer
接口来表示应用程序初始化器。 -
AppInitializersModule
类与AppInitializerEntryPoint
接口之间的关系:AppInitializersModule
类提供了一个AppInitializerEntryPoint
接口的实例。 -
AppInitializersModule
类与AppInitializer
接口之间的关系:AppInitializersModule
类提供了一组AppInitializer
接口的实例。 -
EmojiInitializer
类和UtilsInitializers
类都实现了AppInitializer
接口,它们之间的关系通过实现关系表示。
完整流程
User
启动应用程序。Application
类获取AppInitializerEntryPoint
接口的实例。AppInitializerEntryPoint
接口使用AppInitializersModule
类提供的实例,获取一组应用程序初始化器。AppInitializerEntryPoint
接口调用每个应用程序初始化器的init()
方法,按顺序执行初始化任务。EmojiInitializer
类执行初始化表情符号的任务。UtilsInitializers
类执行初始化实用工具的任务。
总结
很好的利用Hilt + 协程完成启动框架搭建,完美解决代码乱,和初始化策略问题