参考文档
https://developer.android.com/training/dependency-injection/hilt-android
https://medium.com/androiddevelopers/dependency-injection-on-android-with-hilt-67b6031e62d
https://www.zhihu.com/question/32108444

依赖注入

依赖是一个听起来有点唬人的概念. 其实很简单
本来我接受各种参数来构造一个对象,现在只接受一个参数——已经实例化的对象

//不使用依赖注入class A(a:Int,b:Int){val B = B(a,b)}//使用依赖注入class A(val b:B)

所谓依赖, 其实就是成员变量. 之前成员变量在类的内部进行构造, 现在放到了外部. 这就是依赖注入

而Hilt, 就是Android基于Dragger开发的一套依赖注入的框架.

接入

项目根目录build.gradle添加如下代码

buildscript {    ...    dependencies {        ...        classpath 'com.google.dagger:hilt-android-gradle-plugin:2.28-alpha'    }}

在module下的build.gradle修改如下

...apply plugin: 'kotlin-kapt'apply plugin: 'dagger.hilt.android.plugin'android {  compileOptions {    sourceCompatibility JavaVersion.VERSION_1_8    targetCompatibility JavaVersion.VERSION_1_8  }}dependencies {    implementation "com.google.dagger:hilt-android:2.28-alpha"    kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"}

然后这就配置好了. 如果gradle sync的话, 可能还会有一些错误, 提示gradle升级之类的, 按照提示进行操作就可以了.

注解

@HiltAndroidApp

在Application的上面必须使用这个注解. 表示开启依赖注入

@HiltAndroidAppclass ExampleApplication : Application() { ... }

@AndroidEntryPoint

表示这个类可以使用依赖注入项.

@AndroidEntryPointclass ExampleActivity : AppCompatActivity() { ... }

目前AndroidEntryPoint可以在以下来中使用

  • Application
  • Activity
  • Fragment
  • View
  • Service
  • BroadcastReceiver

其中, 如果Fragment使用, 那么包含Fragment的Activity必须也要用该注解
如果View使用, 对应的Fragment和Activity也必须使用.

@Inject

该注解有两个作用

  • 注解在构造函数中, 表示该类可以进行注入
  • 注解在成员变量上, 表示该成员变量使用进行注入
    说的有点绕哈. 直接看代码
class AnalyticsAdapter @Inject constructor() { ... }@AndroidEntryPointclass ExampleActivity : AppCompatActivity() {  @Inject lateinit var analytics: AnalyticsAdapter  ...}

那我们知道Activity都是有生命周期的, 那@Inject的成员变量是在哪一步被注入呢?
这个要注入的Android类有关系. 看下面

组件

对于每个可以注入的Android类, 都有一个关联的Hilt组件, 每个组件负责将其绑定注入相应的Android类
那对应关系如下

  • Application 对应ApplicationComponent组件
  • ViewModel对应ActivityRetainedComponent组件(这一步暂时还不是很懂)
  • Activity对应ActivityComponent组件
  • Fragment对应FragmentComponent组件
  • View对应ViewComponent组件
  • 带@WithFragmentBindings的View对应ViewWithFragmentComponent组件(没有看懂, 没试过)
  • Service对应ServiceComponent组件

注意: Hilt不会为广播接收器生成组件, 因为Hilt直接从ApplicationComponent注入广播接收器(没有看懂)

组件的生命周期

各个组件都有自己的生命周期, 在创建时机中自动创建注入, 在销毁时机中销毁生成的组件实例

组件 创建时机 销毁时机
ApplicationComponent Application#onCreate Application#onDestory
ActivityRetainedComponent Activity#onCreate Activity#onDestory
ActivityComponent Activity#onCreate Activity#onDestory
FragmentComponent Fragment#onAttach Fragment#onDestory
ViewComponent View#super 视图销毁时
ViewWithFragmentComponent View#super 视图销毁时
ServiceComponent Server#onCreate Service#onDestory

注意, ActivityRetainedComponent在配置更改后存在, 他在第一次onCreate的时候创建, 在最后一次onDestroy中销毁, 这和ViewModel的特性保持一致

组件的作用域

怎么理解呢? 默认情况下, 每次请求绑定注入的时候, 都会生成一个新的实例. 但是有些情况不需要, 比如说单例模式. 如下:

@Singletonclass DefaultAccountService @Inject constructor(@ApplicationContext context: Context) : BaseAccountService(context) {}

我们希望DefaultAccountService不要每次都生成一个新的实例, 那么就用@Singleton注解, 标明这是一个单例. 只会执行一次
更详细的信息如下

组件 作用域
ApplicationComponent @Singleton
ActivityRetainedComponent @ActivityRetainedScope
ActivityComponent @ActivityScoped
FragmentComponent @FragmentScoped
ViewComponent @ViewScoped
ViewWithFragmentComponent @ViewSocped
ServiceComponent @ServiceScoped

例如限定作用域为ActivityScoped, 那么在整个Activity的生命周期内, 无论Activity还是Fragment还是View都会提供统一实例

@ViewModelInject

用来注解ViewModel.

@ActivityRetainedScopedclass ZdmViewModel @ViewModelInject constructor(private val adapter:AnalyticsAdapter,@Assisted private val state:SavedStateHandle) : BaseModel(), LifecycleObserver {}

@Module @InstallIn @Provides

对于第三方库, 我们又不能改他的源码, 难道不能用Hilt了吗? 肯定不是
如下:

@Module@InstallIn(ApplicationComponent::class)object AppModule {    @Singleton    @Provides    fun gson(): GsonManager {        return GsonManager.instance()    }}

这里注意一点, 可以看到@InstallIn(ApplicationComponent::class)和@Singleton是一一对应的.
对应关系可以看上面

这里我想放一个大招
其实不仅仅对于三方库. 我们经常头疼Activity Fragment View的数据共享问题, 虽然ViewModel和LiveData的出现帮我们解决了一部分, 但是感觉Activity和View之间依然不顺畅. 因为经常一个View要对应多个Activity.
Hilt的到来解决了这个问题
我们可以针对一个业务逻辑单独拆分出一个ViewMode(而不是简单的一个Activity或者一个Fragment对应一个ViewModel), 然后在Activity和Fragment, View中使用同样的ViewModel即可.

示例:

@ActivityRetainedScopedclass SearchModel @Inject constructor() : BaseModel() {}@AndroidEntryPointclass SearchActivity : ToolbarActivity() {...    @Inject    lateinit var mModel: SearchModel}@AndroidEntryPointclass SearchBar @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : LinearLayout(context, attrs, defStyleAttr) {    @Inject    lateinit var mModel :SearchModel}

如果觉得ViewModel"太大", 也可以直接更新一个LiveData, 除了上述的修改, 增加如下代码即可

@Module@InstallIn(ActivityComponent::class)object AppModuleV2{    @Provides    @ActivityScoped    fun provides(): MutableLiveData<TempUser> {        return MutableLiveData<TempUser>()    }}

@Binds

Binds的作用是将接口与实现类绑定起来,

@InstallIn(ApplicationComponent::class)@Moduleabstract class AppBindModule {    @Binds    abstract fun bindAccountService(defaultAccountService: DefaultAccountService): AccountService}

@ApplicationContext @ActivityContext

这次两个阈值的注解, 通过名称就能看出来, 不详细解释了

@Singletonclass DefaultConfigService @Inject constructor(@ApplicationContext context: Context) : BaseConfigService(context) {}

@EntryPoint

从注解的名称可以看出来, 这个和@AndroidEntryPoint类似, 只不过@AndroidEntryPoint只提供了系统默认的几个类的支持, 如果想让自己实现的类中也可以实现注解, 可以用@EntryPoint, 只不过要稍微麻烦点, 如下

class ServicesManager private constructor(context: Context) {    ...    @EntryPoint    @InstallIn(ApplicationComponent::class)    interface AccountServiceEntryPoint {        fun accountService(): AccountService    }    companion object {        const val ACCOUNT_SERVICE = "account"        const val STATISTICS_SERVICE = "statistics"        const val CONFIG_SERVICE = "config"        val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {            val manager = ServicesManager(LibApplication.instance())            //如下使用            val accountServiceEntryPoint = EntryPointAccessors.fromApplication(LibApplication.instance(), AccountServiceEntryPoint::class.java)            manager.register(ACCOUNT_SERVICE, accountServiceEntryPoint.accountService())            manager        }    }

Hilt的使用场景

此部分copy 扔物线的观点. 扔物线真牛逼.

1. 工具类

毫无疑问, 工具类最适合不过了

2. 数据类

用于Activity Fragment View之间的数据共享, 具体使用办法看上面那一章.

更多相关文章

  1. 初学Android,FrameLayout霓虹灯效果(五)
  2. TimePicker组件&DatePicker组件
  3. android注解框架ButterKnife详细使用文档(v7.0.1)
  4. AndroidUI组件之RoomButton
  5. Android(安卓)Room 数据库
  6. Android在WebView中注入Js代码
  7. AndroidUI组件之RoomButton
  8. android 数据保存与提取
  9. androidの自定义加载对话框ProgressDialog

随机推荐

  1. Drawable、Bitmap、byte[]之间的转换
  2. Android(安卓)Retrofit 笔记之二配置通用
  3. Android开发常用经典代码段集锦
  4. 【Android破解笔记】割绳子2内购
  5. Android(安卓)友盟分享+第三方登录
  6. Android中启动外部程序
  7. Android(安卓)java.lang.IllegalArgument
  8. android opengl es 绘制位图字体
  9. Android(安卓)长按识别图中二维码 zxing
  10. Android.mk 代码注释