Android使用Hilt依赖项注入 - 二

Hilt 是 Android 的依赖项注入库,可减少在项目中执行手动依赖项注入的样板代码。

Android Studio环境为 Android Studio Flamingo | 2022.2.1

Hilt最新版本为2.46

本章我们接着第一节后面继续学习Hilt的相关知识,主要集中在限定符Qualifier和作用域Scope两方面

限定符Qualifier

Hilt为开发者提供了两个预定义限定符,分别为@ActivityContext@ApplicationContext,看名字大致就可以知道是提供Activity上下文和Application上下文的功能,比如我们在别的类中需要引用一个Activity的上下文参数,我们可以直接使用限定符来注入。

@AndroidEntryPoint
class QualifierActivity : BaseActivity<ActHiltBinding>() {

    @Inject
    lateinit var qualifier: Qualifier

    override fun initViewBinding(): ActHiltBinding {
        return ActHiltBinding.inflate(layoutInflater)
    }

    override fun onResume() {
        super.onResume()

        qualifier.test()
    }
}

class Qualifier @Inject constructor(
    @ActivityContext private val context: Context,
    @ApplicationContext private val appContext: Context
) {

    fun test() {
        Log.d("Qualifier", "test ${context.packageName}, ${appContext.packageName}")
        if (context is AppCompatActivity){
            Log.d("Qualifier", "test: ${context::class.simpleName}")
        }
    }
}

# test com.example.myhilt, com.example.myhilt
# test: QualifierActivity

上面的Qualifier类中传入了contextappContext参数,分别用限定符注解,这样我们创建实例的时候就不用单独传入参数了

作用域Scope

在上一节的Module中,我们使用@InstallIn注解来声明了此模块作用的范围,此注解就是告知Hilt该模块在指定的作用域中只会生成一个实例

Android 类生成的组件作用域
ApplicationSingletonComponent@Singleton
ActivityActivityRetainedComponent@ActivityRetainedScoped
ViewModelViewModelComponent@ViewModelScoped
ActivityActivityComponent@ActivityScoped
FragmentFragmentComponent@FragmentScoped
ViewViewComponent@ViewScoped
带有 @WithFragmentBindings 注解的 ViewViewWithFragmentComponent@ViewScoped
ServiceServiceComponent@ServiceScoped

Singleton作用域

使用@Singleton注解之后,在一个应用中周期内只会生成一个实例,通过代码来展示下具体实现

@AndroidEntryPoint
class ScopeActivity : BaseActivity<ActScopeBinding>() {

    @Inject
    lateinit var singletonTest: SingletonTest

    override fun initViewBinding(): ActScopeBinding {
        return ActScopeBinding.inflate(layoutInflater)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        findViewById<Button>(R.id.button).setOnClickListener {
            startActivity(Intent(this, Scope2Activity::class.java))
        }
    }

    override fun onResume() {
        super.onResume()
        singletonTest.test()
    }
}

@Singleton
class SingletonTest @Inject constructor() {

    fun test() {
        Log.d("SingletonTest", "test: $this")
    }
}

@AndroidEntryPoint
class Scope2Activity : BaseActivity<ActScopeBinding>() {

    @Inject
    lateinit var singletonTest: SingletonTest

    override fun initViewBinding(): ActScopeBinding {
        return ActScopeBinding.inflate(layoutInflater)
    }

    override fun onResume() {
        super.onResume()
        singletonTest.test()
    }
}

# test: com.example.myhilt.scope.SingletonTest@196e6e4
# test: com.example.myhilt.scope.SingletonTest@196e6e4

SingletonTest类使用了@Singleton注解修饰,明确了它是一个单例对象,在应用中只会创建独一的实力,分别在两个Activity中都注入了此对象,然后调用它test()方法来打印它的信息,从日志中可以看出两次输出的为同一实例。

Activity作用域

ActivityRetainedScopedActivityScoped这两个都是限定在一个Activity中,但是Retained的范围更广,比如在屏幕旋转时Activity会销毁重建,Retained会保存在内存中,重建之后依然使用的是同一个对象,而ActivityScope会在销毁时也随之销毁,重建会重新创建一个实例,还是通过代码来展示具体实现

@AndroidEntryPoint
class ScopeActivity : BaseActivity<ActScopeBinding>(), DisplayManager.DisplayListener {

    @Inject
    lateinit var singletonTest: SingletonTest

    @Inject
    lateinit var activityScopeTest: ActivityScopeTest

    @Inject
    lateinit var activityRetainedScopeTest: ActivityRetainedScopeTest

    override fun initViewBinding(): ActScopeBinding {
        return ActScopeBinding.inflate(layoutInflater)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        findViewById<Button>(R.id.button).setOnClickListener {
            startActivity(Intent(this, Scope2Activity::class.java))
        }
    }

    override fun onResume() {
        super.onResume()
        val displayManager = this.getSystemService(DISPLAY_SERVICE) as DisplayManager
        displayManager.registerDisplayListener(this, null)
        singletonTest.test()
        activityScopeTest.test()
        activityRetainedScopeTest.test()
    }

    override fun onDisplayAdded(displayId: Int) {
    }

    override fun onDisplayRemoved(displayId: Int) {
    }

    override fun onDisplayChanged(displayId: Int) {
        val display = windowManager.defaultDisplay
        val rotation = display.rotation
        if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
            Log.d(TAG, "onDisplayChanged: 竖屏")
        } else if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
            Log.d(TAG, "onDisplayChanged: 横屏")
        }
    }
}

@ActivityScoped
class ActivityScopeTest @Inject constructor() {

    fun test() {
        Log.d(TAG, "ActivityScopeTest test: $this")
    }
}

@ActivityRetainedScoped
class ActivityRetainedScopeTest @Inject constructor() {
    
    fun test() {
        Log.d(TAG, "ActivityRetainedScopeTest test: $this")
    }
}

# ActivityScopeTest test: com.example.myhilt.scope.ActivityScopeTest@ff1f74d
# ActivityRetainedScopeTest test: com.example.myhilt.scope.ActivityRetainedScopeTest@5edf202
# ActivityScopeTest test: com.example.myhilt.scope.ActivityScopeTest@ce2fd32
# ActivityRetainedScopeTest test: com.example.myhilt.scope.ActivityRetainedScopeTest@5edf202
# onDisplayChanged: 横屏
# ActivityScopeTest test: com.example.myhilt.scope.ActivityScopeTest@5b14b69
# ActivityRetainedScopeTest test: com.example.myhilt.scope.ActivityRetainedScopeTest@5edf202
# onDisplayChanged: 竖屏

ActivityScopeTest类使用@ActivityScoped注解修饰,ActivityRetainedScopeTest类使用@ActivityRetainedScoped注解修饰,并且都在Activity注入,然后我们切换手机的横竖屏,从日志中可以看出,ActivityRetainedScopeTest实例始终为同一个,而ActivityScopeTest则每次旋转都会重新创建一个新的实例,在我们平时开发中,我们可以择优选择,适合自己项目的才是最好的。

其余的作用域都是类似:

  • @ViewModelScoped注解修饰的对象和ViewModel的生命周期是一致的;
  • @FragmentScoped注解修饰的对象和Fragment的生命周期是一致的;
  • @ViewScope注解修饰的有两种,一种就是仅用@ViewSocpe修饰,它的生命周期和View一致,它可以在View中使用,如果View仅用@ActivityScoped注解修改,那么此View不仅可以在Activity中使用,也可以在Fragment中使用,但是如果View额外加上@WithFragmentBindings注解,那么此View只能在Fragment中使用,在Activity中使用时会报一下错误:
@WithFragmentBindings Hilt view must be attached to an @AndroidEntryPoint Fragment

此错误告诉我们,带有@WithFragmentBinding注解的View必须在Fragment中使用,且Fragment必须有@AndroidEntryPoint注解修饰,大家在使用过程中按需添加。

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集

在这里插入图片描述
二、源码解析合集
在这里插入图片描述

三、开源框架合集
在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值