Jackpack - Hilt

一、概念

类中使用的某个对象不是在这个类中实例化的(如Activity无法手动实例化使用),而是通过外部注入(从外部传入对象后使用),这种实现方式就称为依赖注入 Dependency Injection(简称DI)。

构造注入将对象B通过构造传参给classA。有些对象无法通过实例化使用,如Activity。
字段注入将对象C通过函数设置给classA的字段(也叫setter注入、属性注入)。如果类的依赖项非常多,而且要严格执行顺序(如造车前要造好轮子,造轮子又需要先造好螺丝和轮胎),随着项目越发复杂需要编写很多模板代码耦合度也更高,手动注入就容易出错。
方法注入将对象D传入到classA的方法中,仅在该方法中使用。
工厂注入ClassA调用工厂类生产对象调用和生产不在同一个地方,不利于修改测试。
单例注入ClassA调用单例类获取其持有的对象对象的生命周期难以管理,通常并不需要存在于整个APP生命周期,指定在特定的生命周期又需要添加很多判断。

自动注入

基于反射的解决方案,可以在运行时连接依赖项过多使用反射方法会影响程序的运行效率,而且反射方法在编译阶段是不会产生错误的,导致只有在程序运行时才可以验证反射方法是否正确。Square开发的Dagger。
静态解决方案(通过注解),可生成在编译时连接依赖项的代码在编译时就可以发现依赖注入使用的问题。谷歌基于Dagger开发出Dagger2和Hilt,Dagger2使用繁琐,而Hilt专门面向Android开发提供更简单的实现方式,和其它Jetpack组件能更好的协同工作。

二、添加依赖

最新版本

2.1 Project.gradle

plugins {
     id 'com.google.dagger.hilt.android' version "2.44" apply false
}

2.2 app.gradle

plugins {
    id 'com.google.dagger.hilt.android'
}
dependencies {
    implementation 'com.google.dagger:hilt-android:2.44'
    kapt 'com.google.dagger:hilt-compiler:2.44'
}
// Allow references to generated code
kapt {
    correctErrorTypes true
}

三、注解 Application(@HiltAndroidApp)

必须自定义一个Application,并为其添加 @HiltAndroidApp 注解,会触发 Hilt 的代码生成。生成的这一 Hilt 组件会附加到 Application 对象的生命周期,并为其提供依赖项。此外它也是应用的父组件,这意味着其他组件可以访问它提供的依赖项。

四、注入 Android 类(@AndroidEntryPoint)

使用 @AndroidEntryPoint 对以下几种 Android 类添加注解后,就可以向它里面的字段注入依赖了。

  • 为某个 Android 类添加注解,则必须为依赖于该类的其它 Android 类添加注解(例如为 FragmentA 添加注解则必须为所有使用该 FragmentA 的 Activity 添加注解)。 
目前支持的 Android 类使用的注解说明
Activity@AndroidEntryPoint仅支持扩展 ComponentActivity 的 Activity(如AppCompatActivity)。
Fragment仅支持扩展 androidx.Fragment 的 Fragment,不支持保留的 fragment。
View
Service
BroadcastReceiver
ViewModel@HiltViewModel

五、字段注入(@Inject)

声明一个延迟初始化(lateinit var)的属性并添加 @Inject 注解。

  • 注入的字段不能为 private 会导致编译错误。 
@AndroidEntryPoint
class LoginFragment : Fragment() {
    //属性未手动初始化,依赖注入提供了实例,所以不会报错
    @Inject lateinit var logBean: LogBean    //不能为private
}

六、绑定依赖项 - 构造可被注解的情况

向 Hilt 告知如何创建被依赖类型的实例。为被依赖类型的构造函数添加 @Inject 注解,若构造函数有参数,参数类型的构造函数也都要被注解。

//无参
class LogBean @Inject constructor() {}
//有参
data class LogBean @Inject constructor(
    val userName: String,    //又依赖了String类型,String的构造也必须被注解
    val time: TimeBean       //又依赖了TimeBean类型,TimeBean的构造也必须被注解
)

七、绑定依赖项 - 构造无法被注解的情况

向 Hilt 告知如何创建被依赖类型的实例。被依赖类型的构造函数我们无法添加注解(无构造的接口类型、不属于自己的类型如String、Retrofit),就需要手动创建一个模块 Module 并通过函数提供实例。

  • 绑定的作用域必须与其安装到的组件的作用域一致,否则在运行程序时会发生异常。 
注解模块类@Module告知 Hilt 如何提供该类型的实例。
@InstallIn告知 Hilt 模块将用在哪些 Android 类。
注解函数@Binds提供接口实例。必须对抽象函数注解所以类也是抽象的。返回值告知提供哪种接口类型的实例,参数告知该接口的实现类型(该类型也需要对构造注释)。
@Provides提供实例。可以对class注解,若只包含@provides函数定义为object更高效。返回值告知提供哪种类型的实例,参数告知提供的实例还依赖了哪些类型(这些类型也需要对构造注释),函数体告知如何创建实例(每当需要提供实例时都会执行函数体)。

7.1 提供单个实例

7.1.1 提供接口实例 @Binds

interface IWork
class WorkImpl : IWork

@Module
@InstallIn(ActivityComponent::class)
abstract class WorkModule {
    @Binds
    abstract fun bindIWork(workImpl: WorkImpl): IWork
}

7.1.2 提供单个实例 @Provides

@Module
@InstallIn(ActivityComponent::class)
object RetrofitModule {
    @Provides
    fun provideRetrofit(okHeepClient: OkHttpClient): Retrofit {
        return Retrofit.Builder()
            .client(okHeepClient)
            .baseUrl(ApiService.BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
}

7.2 提供多个不同实现的实例

实际开发中可能需要创建同类型的多个不同实现的对象使用,如 Student("张三")  和 Student("李四")、String("A") 和 String(“B”)。上面的方式只能为目标类型提供相同实现的对象,通过使用限定符来实现区分不同实现。

7.2.1 使用 @Named

只需要对 @bind 或 @Provids 注解的函数再使用 @Named 注解,通过传入唯一的 tag 来区分,使用时也要加入对应 tag 让 Hilt 注入的时候选择对应的实例。

@Module
@InstallIn(ActivityComponent::class)
object StringModule {
    @Provides
    @Named("One")
    fun providesOneString() = "One"
    @Provides
    @Named("Two")
    fun providesTwoString() = "Two"
}

@AndroidEntryPoint
class DemoFragment : Fragment() {
    @Inject @Named("One") lateinit var oneString: String
    @Inject @Named("Two") lateinit var twoString: String
}

7.2.2 使用自定义注解 @Qualifier

使用 @Named 方式只能硬编码,因为注解的特性不能穿入一个静态的String,很容易写错或后期重构容易遗漏。先根据需要的分类定义注解,并使用 @Qualifier 修饰来告诉 Hilt 这个注解是用来分类的,其它步骤和 @Named 相似。

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class OneString

@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class TwoString

@Module
@InstallIn(ActivityComponent::class)
object StringModule{
    @Provides
    @OneString
    fun providesOneString(): String = "One"
    @Provides
    @TwoString
    fun providesTwoString(): String = "Two"
}

@AndroidEntryPoint
class DemoFragment : Fragment() {
    @Inject @OneString lateinit var oneString: String
    @Inject @TwoString lateinit var twoString: String
}

八、为Android生成组件

Android类生成的Hilt组件可指定的作用域创建实际~销毁时机
ApplicationApplicationComponent@SingletonApplication:onCreate()~onDestroy()
ViewModelActivityRetainedComponent@ActivityRetainedScopeActivity:onCreate()~onDestroy()
ServiceServiceComponent@ServiceScopedService:onCreate()~onDestroy()
ActivityActivityComponent@ActivityScopedActivity:onCreate()~OnDestroy()
ViewViewComponent@ViewScopedView:super()~视图销毁
FragmentFragmentComponent@FragmentScopedFragment:onAttach()~onDestroy()
@WithFragmentBindings 注解的ViewViewWithFragmentComponent@ViewScopedView:super()~视图销毁

8.1 组件的生命周期

注入一个Android类时都会有关联对应的 Hilt 组件(component),组件也有相同的生命周期不然会内存泄漏。模块会通过 @InstallIn 装载到组件中,组件便可以为 Android 类提供依赖了(对象的创建、注入、销毁)。

8.2 组件的作用域

 默认情况下 Hilt 中所有绑定的依赖项都没有限定作用域,也就是每次代码调用这个字段时都会新建一个实例,当需要共享一个实例时,就需要给依赖项限定作用域(指定的作用域必须跟模块装载到的组件作用域一致,否则报错),即在对应的Android类中为单例。

@Singleton    //指定作用域
class Demo @Inject constructor(){
    fun getString(): String{
        return "Android"
    }
}

@Module
@InstallIn(ActivityComponent::class)
object StringModule {
    @ActivityScoped    //指定作用域
    @Provides
    fun providesOneString() = "One"
}

8.3 组件的层次结构

当一个依赖项的作用域是整个APP,那在Activity中肯定可以访问到,作用域存在包含关系也就是组件存在层次结构。当模块装载到组件后,模块所绑定的依赖项也可以用于该组件层次结构以下的子组件绑定。

 

8.4 组件的默认绑定

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/94231.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JVM 垃圾收集

垃圾收集 分代理论Java 堆的内存分区不同分代收集垃圾收集算法 分代理论 弱分代假说:绝大多数对象都是朝生夕灭,即绝大多数对象都是用完很快需要销毁的。强分代假说:熬过多次垃圾收集过程的对象就越难以消亡,即如果对象经过多次垃…

uniapp热更新

首先热更新需要wgt包; 其次先了解这两个组件 下载的方法 安装的组件 场景: 当你项目的js文件或者页面文件或者静态图片文件css文件更新的时候可以走热更新; 而当你安装新的组件插件或者开启新的权限等功能的时候就无法通过热更新进行更新了…

3、监测数据采集物联网应用开发步骤(3)

监测数据采集物联网应用开发步骤(2) 系统整体结构搭建 新建项目 输入项目名称:MonitorData 所谓兵马未动粮草先行,按下图创建好对应的模块备用: com.plugins 业务插件模块 com.zxy.adminlog 日志或文本文…

合宙Air724UG LuatOS-Air LVGL API控件--按钮 (Button)

按钮 (Button) 按钮控件,这个就不用多说了,界面的基础控件之一。 示例代码 – 按键回调函数 event_handler function(obj, event) if event lvgl.EVENT_CLICKED then print(“Clicked\n”) elseif event lvgl.EVENT_VALUE_CHANGED then print(“To…

云计算 - 百度AIStudio使用小结

云计算 - 百度AIStudio使用小结 前言 本文以ffmpeg处理视频为例,小结一下AI Studio的使用体验及一些避坑技巧。 算力获得 免费的算力获得方式为:每日登录后运行一个项目(只需要点击运行,不需要真正运行)即可获得8小…

K8S Nginx Ingress实现金丝雀发布

通过给 Ingress 资源指定 Nginx Ingress 所支持的 annotation 可实现金丝雀发布。 需给服务创建2个 Ingress,其中1个常规 Ingress,另1个为带 nginx.ingress.kubernetes.io/canary: "true" 固定的 annotation 的 Ingress,称为 Cana…

浅析Linux SCSI子系统:设备管理

文章目录 概述设备管理数据结构scsi_host_template:SCSI主机适配器模板scsi_host:SCSI主机适配器主机适配器支持DIF scsi_target:SCSI目标节点scsi_device:SCSI设备 添加主机适配器构建sysfs目录 添加SCSI设备挂载LunIO请求队列初…

javaee idea创建maven项目,使用el和jstl

如果使用el表达式出现下图问题 解决办法 这是因为maven创建项目时&#xff0c;web.xml头部声明默认是2.3&#xff0c;这个默认jsp关闭el表达式 办法1 在每个需要用到el和jstl的页面的上面加一句: <% page isELIgnored"false" %> 方法2 修改web.xml文件开…

React 生命周期新旧对比

前言 React16.4版本之后使用了新的生命周期&#xff0c;它使用了一些新的生命周期钩子&#xff08;getDerivedStateFromProps、getSnapshotBeforeUpdate&#xff09;&#xff0c;并且即将废弃老版的3个生命周期钩子&#xff08;componentWillMount、componentWillReceiveProps…

【技术】SpringBoot Word 模板替换

SpringBoot Word 模板替换 什么是 Word 模板替换如何实现 Word 模板替换 什么是 Word 模板替换 模板一般是具有固定格式的内容&#xff0c;其中一部分需要替换。Word 模板通俗的讲是以 Word 的形式制作模板&#xff0c;固定格式和内容&#xff0c;然后将其中的一部分数据替换掉…

解决jupyter notebook可以使用pytorch而Pycharm不能使用pytorch的问题

之前我是用的这个目录下的Python 开始更新目录 1、 2、 3、

微信小程序——van-field中的left-icon属性自定义

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

Elasticsearch中倒排索引、分词器、DSL语法使用介绍

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…

1.RabbitMQ介绍

一、MQ是什么&#xff1f;为什么使用它 MQ&#xff08;Message Queue&#xff0c;简称MQ&#xff09;被称为消息队列。 是一种用于在应用程序之间传递消息的通信方式。它是一种异步通信模式&#xff0c;允许不同的应用程序、服务或组件之间通过将消息放入队列中来进行通信。这…

三、Nginx 安装集

一、Nginx CentOS Yum 安装 1. 前置准备 # 默认情况 CentOS-7 中没有 Nginx 的源 # Nginx 官方提供了源&#xff0c;所以执行如下命令添加源 rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm2. 安装 # 安装 yum insta…

1.分布式电源接入对配电网影响分析

分布式电源接入对配电网影响分析 MATLAB代码&#xff1a;分布式电源接入对配电网影响分析 关键词&#xff1a;分布式电源 配电网 评估 参考文档&#xff1a;《自写文档&#xff0c;联系我看》参考选址定容模型部分&#xff1b; 仿真平台&#xff1a;MATLAB 主要内容&a…

限流算法深入

限流定义及目的 当系统流量达到系统或下游承受能力的阈值时对系统进行限流控制以防止系统或下游挂掉&#xff0c;减少影响面。 限流组成&#xff1a;阈值及限流策略。阈值是指系统单位时间接收到的请求qps总数&#xff1b;限流策略是指限流行业触发后对应的系统行为&#xff…

【分布式技术专题】「OSS中间件系列」Minio的文件服务的存储模型及整合Java客户端访问的实战指南

Minio的元数据 数据存储 MinIO对象存储系统没有元数据数据库&#xff0c;所有的操作都是对象级别的粒度的&#xff0c;这种做法的优势是: 个别对象的失效&#xff0c;不会溢出为更大级别的系统失效。便于实现"强一致性"这个特性。此特性对于机器学习与大数据处理非…

初学者必看!我的第一个Invideo人工智能文字生成视频

这是一个使用人工智能生成视频的在线平台。 主要功能包括: - 视频脚本自动生成:可以通过输入主题,由AI自动生成视频故事剧本。 - 人声合成:支持上传脚本,AI会合成自然的人声进行朗读。 - 视频制作:有多种视频模板可选择,支持上传自己的素材,一键生成完整视频。 - 特效和增…

基于Java+SpringBoot+Vue前后端分离美食推荐商城设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…