• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

Android-Hilt实战初体验-Dagger替换成Hilt

android 搞代码 3年前 (2022-03-02) 39次浏览 已收录 0个评论
文章目录[隐藏]

在组件化AwesomeGithub我的项目中应用了Dagger来缩小手动依赖注入代码。尽管它能自动化帮咱们治理依赖项,然而写过之后的应该都会领会到它还是有点繁琐的。我的项目中到处充斥着Component,这让我想起了传统MVP模式的接口定义。

简略来说就是吃力,有许多大量的相似定义。可能google也意识到这一点了,所以前不久公布出了Hilt

Hilt

为了避免没听说过的小伙伴们一头雾水,首先咱们来理解下Hilt是什么?

HiltAndroid的依赖注入库,可缩小在我的项目中执行手动依赖项注入的样板代码。

Hilt通过为我的项目中的每个 Android 类提供容器并主动治理其生命周期,提供了一种在利用中应用 DI(依赖项注入)的规范办法。HiltDagger 的根底上构建而成,因此可能具备 Dagger 的编译时正确性、运行时性能、可伸缩性。

那么有的小伙伴可能会有疑难,既然曾经有了Dagger那为什么还要Hilt的呢?

HiltDagger的次要指标都是统一的:

  1. 简化 Android 利用的 Dagger 相干基础架构。
  2. 创立一组规范的组件和作用域,以简化设置、进步可读性以及在利用之间共享代码。
  3. 提供一种简略的办法来为各种构建类型(如测试、调试或公布)配置不同的绑定。

然而Android中会实例化许多组件类,例如Activity,因而在利用中应用Dagger须要开发者编写大量的样板代码。Hilt能够缩小这些样板代码。

Hilt做的优化包含

  1. 无需编写大量的Component代码
  2. Scope也会与Component主动绑定
  3. 预约义绑定,例如 ApplicationActivity
  4. 预约义的限定符,例如@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

  1. Application (@HiltAndroidApp)
  2. Activity
  3. Fragment
  4. View
  5. Searvice
  6. 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
}

不同的是须要增加@InstallInActivityComponent::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类并且构造方法不能有参数。

@ActivityContextHilt提供的预约限定符,它能提供来自与ActivityContext,对应的还有@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() { ... }

以上是DaggerViewModel应用的注入形式。

尽管自定义的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可能不会那么容易。

庆幸的是,DaggerHilt能够共存。所以你能够选择性的应用。

然而整体而言Hilt真香,你只有尝试了绝不会悔恨~

AwesomeGithub

AwesomeGithub是基于Github的客户端,纯练习我的项目,反对组件化开发,反对账户明码与认证登陆。应用Kotlin语言进行开发,我的项目架构是基于JetPack&DataBinding的MVVM;我的项目中应用了Arouter、Retrofit、Coroutine、Glide、Dagger与Hilt等风行开源技术。

除了Android原生版本,还有基于Flutter的跨平台版本flutter_github。


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:Android-Hilt实战初体验-Dagger替换成Hilt

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址