在组件化 AwesomeGithub 我的项目中应用了 Dagger
来缩小手动依赖注入代码。尽管它能自动化帮咱们治理依赖项,然而写过之后的应该都会领会到它还是有点繁琐的。我的项目中到处充斥着 Component
,这让我想起了传统MVP
模式的接口定义。
简略来说就是 吃力 ,有许多大量的相似定义。可能google
也意识到这一点了,所以前不久公布出了Hilt
。
Hilt
为了避免没听说过的小伙伴们一头雾水,首先咱们来理解下 Hilt
是什么?
Hilt
是 Android
的依赖注入库,可缩小在我的项目中执行手动依赖项注入的样板代码。
Hilt
通过为我的项目中的每个 Android
类提供容器并主动治理其生命周期,提供了一种在利用中应用 DI
(依赖项注入)的规范办法。Hilt
在Dagger
的根底上构建而成,因此可能具备 Dagger
的编译时正确性、运行时性能、可伸缩性。
那么有的小伙伴可能会有疑难,既然曾经有了 Dagger
那为什么还要 Hilt
的呢?
Hilt
与 Dagger
的次要指标都是统一的:
- 简化
Android
利用的Dagger
相干基础架构。 - 创立一组规范的组件和作用域,以简化设置、进步可读性以及在利用之间共享代码。
- 提供一种简略的办法来为各种构建类型(如测试、调试或公布)配置不同的绑定。
然而 Android
中会实例化许多组件类,例如 Activity
,因而在利用中应用Dagger
须要开发者编写大量的样板代码。Hilt
能够缩小这些样板代码。
Hilt
做的优化包含
- 无需编写大量的
Component
代码 -
Scope
也会与Component
主动绑定 - 预约义绑定,例如
Application
与Activity
- 预约义的限定符,例如
@ApplicationContext
与@ActivityContext
上面通过 AwesomeGithub 中 Dagger
来比照理解 Hilt
的具体应用。
依赖
应用之前将 Hilt
的依赖增加到我的项目中。
首先,将 hilt-android-gradle-plugin
插件增加到我的项目的根级 build.gradle
文件中:
buildscript {
...
dependencies {
...
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'
}
}
而后,利用 Gradle
插件并在 app/build.gradle
文件中增加以下依赖项:
...
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
android {...}
dependencies {
implementation "com.google.dagger:hilt-android:2.28-alpha"
kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"
}
Application 类
应用 Dagger
时,须要一个 AppComponent
单例组件,我的项目中的其它 SubComponent
都将依赖于它,所以在 AwesomeGithub 中它大略是这个样子
@Singleton
@Component(
modules = [
SubComponentModule::class,
NetworkModule::class,
ViewModelBuilderModule::class
]
)
interface AppComponent {
@Component.Factory
interface Factory {fun create(@BindsInstance applicationContext: Context): AppComponent
}
fun welcomeComponent(): WelcomeComponent.Factory
fun mainComponent(): MainComponent.Factory
...
fun loginComponent(): LoginComponent.Factory}
@Module(
subcomponents = [
WelcomeComponent::class,
MainComponent::class,
...
LoginComponent::class
]
)
object SubComponentModule
下面的我曾经省略了大半,是不是看起来很多,而且最重要的是很多反复的构造根本都是一样的。所以 Hilt
基于这一点进行了简化,将这些反复的编写转成构建的时候主动生成。
Hilt
要做的很简略,增加几个正文
@HiltAndroidApp
class App : Application() { ...}
所有的 Hilt
利用都必须蕴含一个带 @HiltAndroidApp
正文的 Application
。它将代替Dagger
中的AppComponent
。
Android 类
对于 Android
类,应用 Dagger
时须要定义 SubComponent
并将它依赖到 Application
类中。上面以 WelcomeActivity
为例。
@Subcomponent(modules = [WelcomeModule::class])
interface WelcomeComponent {
@Subcomponent.Factory
interface Factory {fun create(): WelcomeComponent
}
fun inject(activity: WelcomeActivity)
}
module 的局部先不说,前面会提及
上面看 Hilt
的实现
@AndroidEntryPoint
class MainActivity : BaseHiltActivity<ActivityMainBinding, MainVM>() { ...}
Hilt
要做的是增加 @AndroidEntryPoint
正文即可。
诧异,联合下面的,两个注解就替换了 Dagger
的实现,当初是否领会到 Hilt
的简洁?对老手来说也能够升高很大的学习老本。
目前 Hilt
反对上面 Android
类
- Application (@HiltAndroidApp)
- Activity
- Fragment
- View
- Searvice
- BroadcastReceiver
有一点须要留神,如果应用 @AndroidEntryPoint
正文了某个类,那么依赖该类的其它类也须要增加。
典型的就是 Fragment
,所以除了Fragment
还须要给依赖它的所有 Activity
进行正文。
@AndroidEntryPoint
的作用,对照一下 Dagger
就晓得了。它会主动帮咱们生成对应 Android
类的 Componennt
,并将其增加到Application
类中。
@Inject
@Inject
的应用根本与 Dagger
统一,能够用来定义构造方法或者字段,申明该构造方法或者字段须要通过依赖获取。
class UserRepository @Inject constructor(private val service: GithubService) : BaseRepository() { ...}
@Module
Hilt
模块也须要增加 @Module
正文,与 Dagger
不同的是它还必须应用 @InstallIn
为模块增加正文。目标是告知模块用在哪个 Android
类中。
@Binds
@Binds
正文会告知 Hilt
在须要提供接口的实例时要应用哪种实现。
它的用法与 Dagger
没什么区别
@Module
@InstallIn(ActivityComponent::class)
abstract class WelcomeModule {
@Binds
@IntoMap
@ViewModelKey(WelcomeVM::class)
abstract fun bindViewModel(viewModel: WelcomeVM): ViewModel
}
不同的是须要增加 @InstallIn
,ActivityComponent::class
用来表明该模块作用范畴为Activity
其实下面这块对
ViewModel
的注入,应用Hilt
时会主动帮咱们编写,这里只是为了展现与Dagger
的不同之处。后续会提到ViewModel
的注入。
@Providers
提供一个 FragmentManager
的实例,首先是 Dagger
的应用
@Module
class MainProviderModule(private val activity: FragmentActivity) {
@Provides
fun providersFragmentManager(): FragmentManager = activity.supportFragmentManager}
比照一下Hilt
@InstallIn(ActivityComponent::class)
@Module
object MainProviderModule {
@Provides
fun providerFragmentManager(@ActivityContext context: Context) = (context as FragmentActivity).supportFragmentManager
}
区别是在 Hilt
中@Providers
必须为 static
类并且构造方法不能有参数。
@ActivityContext
是Hilt
提供的预约限定符,它能提供来自与Activity
的Context
,对应的还有@ApplicationContext
提供的组件
对于之前提到的 @InstallIn
会关联不同的 Android
类,除了 @ActivityComponent
还有以下几种
对应的生命周期如下
同时还提供了相应的作用域
所以 Hilt
的默认提供将大幅提高开发效率,缩小许多反复的定义
ViewModel
最初再来说下 ViewModel
的注入。如果你应用到了 Jetpack
置信少不了它的注入。
对于 Dagger
咱们须要自定义一个 ViewModelFactory
,并且提供注入形式,例如在 AwesomeGithub 的componentbridget
模块中定义了ViewModelFactory
@Module
abstract class ViewModelBuilderModule {
@Binds
abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
}
class ViewModelFactory @Inject constructor(private val creators: @JvmSuppressWildcards Map<Class<out ViewModel>, Provider<ViewModel>>) : ViewModelProvider.Factory {override fun <T : ViewModel?> create(modelClass: Class<T>): T {var creator = creators[modelClass]
if (creator == null) {for ((key, value) in creators) {if (modelClass.isAssignableFrom(key)) {creator = value}
}
}
if (creator == null) {throw IllegalArgumentException("Unknown model class: $modelClass")
}
try {@Suppress("UNCHECKED_CAST")
return creator.get() as T} catch (e: Exception) {throw RuntimeException()
}
}
}
通过 @Inject
来注入结构实例,但构造方法中须要提供 Map
类型的 creators
。这个时候能够应用@IntoMap
,为了匹配Map
的类型,须要定义一个 @MapKey
的正文
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
而后再到对应的组件下应用,例如匹配MainVM
@Module
abstract class MainModule {
@Binds
@IntoMap
@ViewModelKey(MainVM::class)
abstract fun bindViewModel(viewModel: MainVM): ViewModel
}
这样就提供了 Map<Class<MainVM>, MainVM>
的参数类型,这时咱们自定义的 ViewModelFactory
就可能被胜利注入。
例如 basic
模块外面的BaseDaggerActivity
abstract class BaseDaggerActivity<V : ViewDataBinding, M : BaseVM> : AppCompatActivity() {
protected lateinit var viewDataBinding: V
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
protected val viewModel by lazy {ViewModelProvider(this, viewModelFactory)[getViewModelClass()] }
...
}
当然,别忘了 MainVM
也须要应用 @Inject
来申明注入
class MainVM @Inject constructor() : BaseVM() {...}
以上是 Dagger
的ViewModel
应用的注入形式。
尽管自定义的 ViewModelFactory
是专用的,然而对于不同的 ViewModel
还是要手动定义不同的 bindViewModel
办法。
而对于 Hilt
却能够省略这一步,甚至说下面的全副都不须要手动编写。咱们须要做的是只需在 ViewModel
的构造函数上增加@ViewModelInject
。
例如下面的 MainVM
,应用Hilt
的成果如下
class MainVM @ViewModelInject constructor() : BaseVM() {...}
至于 Hilt
为什么会这么简略呢?咱们不要忘了它的实质,它是在 Dagger
之上建设的,实质是为了帮忙咱们缩小不必要的样板模板,不便开发者更好的应用依赖注入。
在 Hilt
中,下面的实现会主动帮咱们生成,所以才会应用起来这么简略。
如果你去比照看 AwesomeGithub 上的 feat_dagger 与 feat_hilt 两个分支中的代码,就会发现应用 Hilt
显著少了许多代码。对于简略的 Android
类来说就是减少几个正文而已。
目前惟一一个比拟不现实的是对于 @Providers
的应用,构造方法中不能有参数,如果在用 Dagger
应用时曾经有参数了,再转变成 Hilt
可能不会那么容易。
庆幸的是,Dagger
与 Hilt
能够共存。所以你能够选择性的应用。
然而整体而言 Hilt
真香,你只有尝试了绝不会悔恨~
AwesomeGithub
AwesomeGithub 是基于 Github 的客户端,纯练习我的项目,反对组件化开发,反对账户明码与认证登陆。应用 Kotlin 语言进行开发,我的项目架构是基于 JetPack&DataBinding 的 MVVM;我的项目中应用了 Arouter、Retrofit、Coroutine、Glide、Dagger 与 Hilt 等风行开源技术。
除了 Android 原生版本,还有基于 Flutter 的跨平台版本 flutter_github。
如果你喜爱我的文章模式,或者对我接下来的文章感兴趣,你能够关注我的微信公众号:【Android 补给站】
或者扫描下方二维码,与我建设无效的沟通,同时可能更不便的收到相干的技术推送。