Jackpack - Hilt

news2024/10/5 23:29:31

一、概念

类中使用的某个对象不是在这个类中实例化的(如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 组件的默认绑定

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软考A计划-网络工程师-常用计算公式汇总

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 👉关于作者 专注于Android/Unity和各种游…

科技探究之旅--亲子研学活动

2023年8月26日,广州市从化区齐家社会工作服务中心(以下简称“齐家”)的“星乐园-乡村儿童公益辅导服务项目”组织了新开村及西湖村助学点24对亲子到广州市白云区文搏3D打印基地进行“科技探究之旅--亲子研学”活动,旨在发现、点燃…

限流算法深入

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

WPF+Prism+WebApi 学习总结

一、基本概念 WPF:WPF(Windows Presentation Foundation)是(微软推出的)基于Windows的用户界面框架,提供了统一的编程模型,语言和框架,做到了分离界面设计人员与开发人员的工作;WPF…

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

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

JavaScript—数据类型、对象与构造方法

js是什么? JavaScript(简称“JS”) 是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。JavaScript 基于原型编程、多范式的动态脚本语言,并且支持面向对象、命令式、声明式、函数式编程范式。 js有哪些特点呢…

exchange实战

未得到exchange服务器权限 确定exchange服务器ip地址 setspn -T example.domain.com -F -Q */* | findstr exchangeMailSniper 爆破用户名和密码 爆破Exchange邮箱用户名密码,为了防止账号被锁定,所以我们使用密码喷洒攻击,即只使用一个密…

Windows上安装Hadoop 3.x

目录 0. 安装Java 1. 安装Hadoop 1.1 下载Hadoop 1.2 下载winutils 2. 配置Hadoop 1. hadoop-env.cmd 2. 创建数据目录 3. core-site.xml 4. hdfs-site.xml 3. 启动测试 3.1 namenode格式化 3.2 启动Hadoop 3.3 查看webui 3.4 测试hdfs 3.5. 测试MapReduce 4. 还…

构建个人博客_Obsidian_github.io_hexo

1 初衷 很早就开始分享文档,以技术类的为主,一开始是 MSN,博客,随着平台的更替,后来又用了 CSDN,知乎,简书…… 再后来是 Obsidian,飞书,Notion,常常有以下困…

《Flink学习笔记》——第八章 状态管理

8.1 Flink中的状态 8.1.1 概述 在Flink中,算子任务可以分为无状态和有状态两种情况。 **无状态的算子:**每个事件不依赖其它数据,自己处理完就输出,也不需要依赖中间结果。例如:打印操作,每个数据只需要…

【PLSQL】PLSQL基础

文章目录 一:记录类型1.语法2.代码实例 二:字符转换三:%TYPE和%ROWTYPE1.%TYPE2.%ROWTYPE 四:循环1.LOOP2.WHILE(推荐)3.数字式循环 五:游标1.游标定义及读取2.游标属性3.NO_DATA_FOUND和%NOTFO…

释放 ChatGPT 的价值:5 个专家提示

随着近来ChatGPT的热议,人工智能技术被推上风口浪尖,由此以数字化技术为基础的数字营销也再次受到了不小的关注,但是营销的本质从来都没有变过,今天我们聊下ChatGPT无论如何演进,人工智能无论变得多么先进,…

设计模式—外观模式(Facade)

目录 一、什么是外观模式? 二、外观模式具有什么优点吗? 三、外观模式具有什么缺点呢? 四、什么时候使用外观模式? 五、代码展示 ①、股民炒股代码 ②、投资基金代码 ③外观模式 思维导图 一、什么是外观模式?…

《Flink学习笔记》——第四章 Flink运行时架构

4.1 系统架构 Flink运行时架构 Flink 运行时由两种类型的进程组成:一个 JobManager 和一个或者多个 TaskManager。 1、作业管理器(JobManager) JobManager是一个Flink集群中任务管理和调度的核心,是控制应用执行的主进程。也就…

Java之API详解之Object类的详细解析

4 Object类 4.1 概述 tips:重点讲解内容 查看API文档,我们可以看到API文档中关于Object类的定义如下: Object类所在包是java.lang包。Object 是类层次结构的根,每个类都可以将 Object 作为超类。所有类都直接或者间接的继承自该类…

JavaScript设计模式(二)——简单工厂模式、抽象工厂模式

个人简介 👀个人主页: 前端杂货铺 🙋‍♂️学习方向: 主攻前端方向,正逐渐往全干发展 📃个人状态: 研发工程师,现效力于中国工业软件事业 🚀人生格言: 积跬步…

网络防御和入侵检测

网络防御和入侵检测是维护网络安全的关键任务,可以帮助识别和阻止未经授权的访问和恶意行为。以下是一些基本的步骤和方法,用于进行网络防御和入侵检测。 网络防御: 防火墙设置: 部署防火墙来监控和控制网络流量,阻止…

禅道后台命令执行漏洞 (二)

漏洞简介 禅道是第一款国产的开源项目管理软件。它集产品管理、项目管理、质量管理、文档管理、 组织管理和事务管理于一体,是一款专业的研发项目管理软件,完整地覆盖了项目管理的核心流程。禅道管理思想注重实效,功能完备丰富,操…

多线程基础篇

我们平常说的一个程序,一个程序中有声音,图片,字幕 实际上是一个进程中有多个线程 main线程是主线程。 多核,多个cpu,多个线程,切换的很快 单核的话是一个cpu,某一时间只能是一个线程,但是因为…

NLNet、GCNet、RTNet三种多头注意力网络的对比与分析

目录 一、中心思想 二、网络结构的异同点 三、网络结构的改进 3.1 GCNet的改进 3.2 RTNet的改进 四、总结 一、中心思想 三种网络最终目的都是为了捕获远程依赖关系或是全局上下文信息以增强目标检测或是目标分割的效果。 NLNet:卷积运算只能一次处理一个局…