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 {@Bindsabstract fun bindIWork(workImpl: WorkImpl): IWork
}

7.1.2 提供单个实例 @Provides

@Module
@InstallIn(ActivityComponent::class)
object RetrofitModule {@Providesfun 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@OneStringfun providesOneString(): String = "One"@Provides@TwoStringfun 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    //指定作用域@Providesfun providesOneString() = "One"
}

8.3 组件的层次结构

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

 

8.4 组件的默认绑定

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

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

相关文章

公网远程访问局域网SQL Server数据库

文章目录 1.前言2.本地安装和设置SQL Server2.1 SQL Server下载2.2 SQL Server本地连接测试2.3 Cpolar内网穿透的下载和安装2.3 Cpolar内网穿透的注册 3.本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4.公网访问测试5.结语 1.前言 数据库的重要性相信大家都有所了解&…

XML—DTD、 Schema

目录 DTD是什么? DTD有什么用途? DTD与XML有什么联系? DTD原理图 外部DTD DTD文件book.dtd: 使用外部DTD文件的XML文件 PCDATA XML 文档构建模块 一、元素 1、元素声明 ①、有元素: ②、空元素: ③、ANY…

【LeetCode-中等题】24. 两两交换链表中的节点

文章目录 题目方法一:递归方法二:三指针迭代 题目 方法一:递归 图解: 详细版 public ListNode swapPairs(ListNode head) {/*递归法:宗旨就是紧紧抓住原来的函数究竟返回的是什么?作用是什么即可其余的细枝末节不要细究,编译器…

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. 还…

WPF基础入门-Class3-WPF数据模板

WPF基础入门 Class3&#xff1a;WPF数据模板 1、先在cs文件中定义一些数据 public partial class Class_4 : Window{public Class_4(){InitializeComponent();List<Color> test new List<Color>();test.Add(new Color() { Code "Yellow", Name &qu…

客户体验、在线帮助中心、官网博客,这三者有何关联?

客户体验与在线帮助中心和官网博客之间存在一定的联系&#xff0c;但它们并不完全等同。下面将分别介绍这三者的关系和作用。 客户体验&#xff1a; 客户体验是指客户在与企业进行交互时所感受到的整体体验。它包括客户在使用产品或服务过程中的感受、情感、满意度和忠诚度等…

平衡二叉树(AVL树)C++

目录 AVL树的概念 AVL树的节点结构 AVL树的插入 更新平衡节点 代码实现 AVL树的旋转 左单旋 右单旋 左右双旋 右左双旋 AVL树的删除 AVL树的查找 AVL树的高度 AVL树的判定 AVL树的遍历 AVL树的概念 二叉排序&#xff08;搜索&#xff09;树&#xff0c;虽然可以…

原生微信小程序 动态(横向,纵向)公告(广告)栏

先看一下动态效果 Y轴滚动公告的原理是swiper组件在页面中的Y轴滚动&#xff0c;属性vertical&#xff0c;其余属性也设置一下autoplay circular interval"3000" X轴滚动的原理是&#xff0c;利用动画效果&#xff0c;将内容从右往左过渡过去 wxml&#xff1a; &l…

在CSS中,盒模型中的padding、border、margin是什么意思?

在CSS中&#xff0c;盒模型&#xff08;Box Model&#xff09;是用来描述和布局HTML元素的基本概念。它将每个HTML元素看作是一个矩形的盒子&#xff0c;这个盒子包括了内容&#xff08;content&#xff09;、内边距&#xff08;padding&#xff09;、边框&#xff08;border&a…

Python使用 YOLO_NAS_S 模型进行目标检测并保存预测到的主体图片

一、前言&#xff1a; 使用 YOLO_NAS_S 模型进行目标检测&#xff0c;并保存预测到的主体图片 安装包&#xff1a; pip install super_gradients pip install omegaconf pip install hydra-core pip install boto3 pip install stringcase pip install typing-extensions pi…

外部库/lib/maven依赖项 三者关系

外部库(存放项目初始配置的jar包)(它的文件夹里并没有包含lib文件夹的引的外部的依赖的jar包) lib(存放外部导入到项目的依赖的jar包) maven依赖项(管理项目所有的jar包依赖) 三者存放jar包的关系 项目所依赖的全部的jar包 maven依赖项的jar包 外部库中的jar包 lib中的…

指针进阶详解

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言程序设计————KTV C语言小游戏 C语言进阶 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力&#xff0c;一起奔赴大厂。 目录 1.字符指针 2.指针数组 3.数组指针 4.数组传…

HTTP 框架修炼之道 | 青训营

Powered by:NEFU AB-IN 文章目录 HTTP 框架修炼之道 | 青训营 走进 HTTP 协议HTTP 框架的设计与实现应用层中间件层路由设计协议层 传输层&#xff08;网络层&#xff09;1. BIO&#xff08;Blocking I/O&#xff09;:2. NIO&#xff08;Non-blocking I/O&#xff09;:区别&…

跳跃游戏 II【贪心算法】

跳跃游戏 II class Solution {public int jump(int[] nums) {int cur 0;//当前最大覆盖路径int next 0;//下一步的最大覆盖路径int res 0;//存放结果&#xff0c;到达终点时最少的跳跃步数for (int i 0; i < nums.length; i) {//遍历数组&#xff0c;以给出数组以一个…

第七周第七天学习总结 | MySQL入门及练习学习第二天

实操练习&#xff1a; 1.创建一个名为 cesh的数据库 2.在这个数据库内 创建一个名为 xinxi 的表要求该表可以包含&#xff1a;编号&#xff0c;姓名&#xff0c;备注的信息 3.为 ceshi 表 添加数据 4.为xinxi 表的数据设置中文别名 5.查询 在 xinxi 表中编号 为2 的全部…

网络编程嵌套字

网络编程 程序员主要操作应用层和传输层来实现网络编程 也就是自己写一个程序&#xff0c;让这个程序可以使用网络来通信 这个程序属于应用层&#xff0c;实现通讯就需要获取到传输层提供的服务 这就需要使用传输层提供的api UDP&#xff1a;无连接&#xff0c;不可靠传输&a…

css background实现四角边框

2023.8.27今天我学习了如何使用css制作一个四角边框&#xff0c;效果如下&#xff1a; .style{background: linear-gradient(#33cdfa, #33cdfa) left top,linear-gradient(#33cdfa, #33cdfa) left top,linear-gradient(#33cdfa, #33cdfa) right top,linear-gradient(#33cdfa, #…

Viobot基本功能使用及介绍

设备拿到手当然是要先试一下效果的&#xff0c;这部分可以参考本专栏的第一篇 Viobot开机指南。 接下来我们就从UI开始熟悉这个产品吧&#xff01; 1.状态 设备上电会自动运行它的程序&#xff0c;开启了一个服务器&#xff0c;上位机通过连接这个服务器连接到设备&#xff0c…

无涯教程-分类算法 - 多项式逻辑回归模型函数

Logistic逻辑回归的另一种有用形式是多项式Lo​​gistic回归&#xff0c;其中目标或因变量可以具有3种或更多可能的unordered类型&#xff0c;即没有定量意义的类型。 用Python实现 现在&#xff0c;无涯教程将在Python中实现上述多项式逻辑回归的概念。为此&#xff0c;使用…

学习Linux基础知识与命令行操作

开始学习Linux系统前&#xff0c;首先要掌握计算机基础知识&#xff0c;了解硬件、操作系统、文件系统、网络和安全等概念。对这些基础知识的了解能够帮助理解Linux系统的概念和功能。 在Linux系统中&#xff0c;文件和目录是数据管理的基本单位。每个文件和目录都有一个称为&…