Android _ 从 Dagger2 到 Hilt 玩转依赖注入(一)(依赖注入模式)

Android _ 从 Dagger2 到 Hilt 玩转依赖注入(一)APT 【点赞催更】Java | 注解(含 Kotlin) 1. 为什么要进行依赖注入
依赖注入(Dependency In

APT [点赞更新] Java 注解(包括Kotlin)

1. 为什么要进行依赖注入

依赖注入(DI)实际上并不是那么神秘的概念。依赖注入应用“控制反转”(IoC)的原理。这仅仅意味着使用构造函数或setter 注入在类外部构建依赖项。

提示:人们经常错误地使用依赖注入的思想。

使用依赖注入有什么好处?

组件重用:因为您在类之外构建依赖项,所以组件隔离:如果您需要更改组件的实现,您可以注入依赖项的模拟实现,而无需对项目进行大量更改。纳入依赖方可以更轻松地测试依赖方。生命周期透明:依赖方不知道依赖项的创建/销毁生命周期,并且可以由依赖项注入框架进行管理。

2. Android 依赖注入框架

当只有一个依赖项时,手动依赖项注入很简单,但随着项目规模的增加,手动依赖项注入会变得越来越复杂。依赖注入框架使依赖注入过程变得更加容易。此外,依赖项注入框架通常提供管理依赖项生命周期的功能。从实现的角度来看,依赖注入框架可以分为两类:

1. 基于反射的动态解决方案:Guice、Dagger;2. 基于编译时注释的静态解决方案(高性能):Dagger2、Hilt、ButterKnife。

提示:依赖注入框架本身并不提供依赖注入功能,而是使用注释和其他方法来促进依赖注入。

今天我们就来聊聊Dagger2和Hilt。

Dagger2:Dagger以有向无环图(DAG)命名,最初由Square组织开发。 Dagger2 和Hilt 框架随后由Square 和Google 联合开发和维护。

Hilt:Hilt是Dagger2的二次封装。它本质上是Dagger 的基于场景的版本。为Android平台建立了一套规则,大大简化了Dagger2的使用。与Dagger2 需要您手动检索依赖图并执行注入操作不同,Hilt 会自动查找Android 系统组件中的最佳注入位置,因此注入会自动发生。

接下来,我们将分别讨论两个框架:Dagger2 和Hilt。最初,我并没有打算过多介绍Dagger2(因为Android 直接使用Hilt)。考虑到两者之间的关系,我们觉得为了真正理解Hilt给我们带来了什么,我们需要清楚地解释Dagger2。

3. Dagger2 使用教程

提示:我在学习Dagger2的时候,也看了很多文章和官方文档。有些作者列出了所有注释的用法,而另一些作者只提供用法并忽略自动生成的代码描述。我也在寻找一种易于理解和接受的教学方法,但我认为如果从“基本注释”开始,然后解释如何自动生成“复杂注释”,会更容易理解。我将引导您完成代码并向您展示如何使用它。我们期待听到您的意见~

在讨论过程中,我们将扩展这个简单的示例。假设您有一个依赖于两个依赖项的用户数据模块。

公共类用户存储库{

私有最终UserLocalDataSource userLocalDataSource;

私有最终UserRemoteDataSource userRemoteDataSource;

公共UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) {

this.userLocalDataSource=userLocalDataSource;

this.userRemoteDataSource=userRemoteDataSource;

}

}

首先,您可以选择不使用依赖注入。在这种情况下,您可能需要在项目中的多个位置重复构建它。它的缺点已在第一节中讨论过。

新的UserRepository(新的UserLocalDataSource(),新的UserRemoveDataSource());

然后我开始使用依赖注入并创建全局工具方法。

公共静态UserRepository get() {

返回新的UserRepository(新的UserLocalDataSource(),新的UserRemoveDataSource());

}

虽然这满足了我们的要求,但在实际项目中,模块之间的依赖关系往往比这个例子复杂得多。目前,频繁手工编写依赖注入模板代码不仅费时费力,而且容易出错。接下来,使用Dagger2 帮助程序创建模板代码。

3.1 @Component + @Inject

@Component和@Inject是Dagger2中两个最基本的注解。只需使用这两个注释就可以实现最简单的依赖注入。

@Component:创建Dagger 容器作为检索依赖项的入口点。

@成分

公共接口应用程序组件{

UserRepository userRepository();

}

@Inject:告诉Dagger如何实例化对象。

公共类用户存储库{

私有最终UserLocalDataSource userLocalDataSource;

私有最终UserRemoteDataSource userRemoteDataSource;

@注入

公共UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) {

this.userLocalDataSource=userLocalDataSource;

this.userRemoteDataSource=userRemoteDataSource;

}

}

公共类用户本地数据源{

@注入

公共用户本地数据源(){

}

}

公共类用户删除数据源{

@注入

公共用户删除数据源(){

}

}

您必须使用@Inject 注释来装饰依赖项的构造函数。同时,还必须在其依赖项UserLocalDataSource和UserRemoteDataSource中添加@Inject注解。

上面的代码构建后会自动生成代码。

DaggerApplicationComponent.java

1. 实现ApplicationComponent接口

公共最终类DaggerApplicationComponent 实现ApplicationComponent {

私人DaggerApplicationComponent() {

}

2. 创建依赖实例

@覆盖

公共UserRepository userRepository() {

返回新的UserRepository(新的UserLocalDataSource(),新的UserRemoteDataSource());

}

3.建造者模式

公共静态构建器构建器(){

返回新的构建器();

}

公共静态ApplicationComponent创建(){

返回一个新的Builder().build()。

}

公共静态最终类生成器{

私有构建器(){

}

公共应用程序组件构建(){

返回一个新的DaggerApplicationComponent()。

}

}

}

正如你所看到的,最简单的依赖注入模板代码已经自动为你生成了。使用此功能时,您所需要做的就是通过ApplicationComponent 条目获取UserReopsitory 实例。

ApplicationComponent 组件=DaggerApplicationComponent.create();

UserRepository userRepository=component.userRepository();

3.2 @Inject 字段注入

有些类不是使用构造函数初始化的。例如,Android框架类Activity和Fragment是由系统实例化的。此时3.1节中使用的构造函数注入已经不能使用了,可以改为字段注入。调用请求注入的方法。

构造函数注入:(X)

公共类我的活动{

@注入

公共MyActivity(LoginViewModel viewModel){

……

}

}

现场注入:

类MainActivity : AppCompatActivity() {

@注入

Lateinit var viewModel: LoginViewModel

覆盖fun onCreate(savedInstanceState: Bundle?) {

DaggerApplicationComponent.create().inject001(this)

super.onCreate(savedInstanceState)

……

}

}

公共类LoginViewModel {

私有最终UserRepository userRepository;

@注入

公共LoginViewModel(UserRepository userRepository) {

this.userRepository=用户存储库;

}

}

在活动或片段中使用组件时,您必须了解组件的生命周期。

在super.onCreate() 的恢复阶段,活动会附加可能需要访问该活动的绑定片段。为了确保数据一致性,您必须在调用super.onCreate() 之前将Dagger 注入到Activity 的onCreate() 方法中。

使用fragment时,必须将Dagger注入到fragment的onAttach()方法中。这可以在调用super.onAttach() 之前或之后完成。

3.3 @Singleton / @Scope

@Singleton/@Scope:声明一个作用域,可以限制依赖的作用域循环。

@单例

公共类用户存储库{

……

}

@成分

@单例

公共接口应用程序组件{

……

}

在ApplicationComponent 和UserRepository 上使用相同的作用域注释来指示它们处于同一作用域循环中。这意味着同一组件多次提供同一依赖项实例。您可以直接使用内置的@Singleton或使用自定义注释。

@范围

@记录

@hold(运行时)

公共@interface单例{}

@范围

@Retention(RetentionPolicy.RUNTIME)

公共@interfaceMyCustomScope{}

提示:使用@Singleton 或@MyCustomScope 具有完全相同的效果。

上面的代码构建后会自动生成代码。

公共最终类DaggerApplicationComponent 实现ApplicationComponent {

私有提供者userRepositoryProvider;

私人DaggerApplicationComponent() {

初始化();

}

私有无效初始化(){

this.userRepositoryProvider=DoubleCheck.provider(UserRepository_Factory.create(UserLocalDataSource_Factory.create(), UserRemoteDataSource_Factory.create()));

}

@覆盖

公共UserRepository userRepository() {

返回userRepositoryProvider.get();

}

……

}

作用域注解约束

您应该注意范围注释的一些限制。

如果组件具有范围注释,则该组件只能提供具有该注释的类,也可以提供不具有范围注释的类。子组件不能使用与父组件相同的范围注释。

提示:有关子组件的概念,请参阅第3.5 节。

作用域注解规范

Dagger2框架并不严格限制你定义的作用域语义,只要你满足上面描述的约束规则即可。您可以按业务划分范围,也可以按生命周期划分范围。例如:

按照划分,

@单例

@登录范围

@RegisterScope

按生命周期细分如下。

@单例

@ActivityScope

@ModuleScope

@FeatureScope

不过,按照生命周期来划分范围是比较理想的,并且范围不应该明确表明实现的目的。

3.4 @Module + @Providers

@Module + @Providers:告诉Dagger如何实例化对象而不是构造函数。

公共类UserRemoteDataSource {

私有最终LoginRetrofitService LoginRetrofitService;

@注入

公共UserRemoteDataSource(LoginRetrofitServiceloginRetrofitService){

this.loginRetrofitService=loginRetrofitService;

}

}

@模块

公共类网络模块{

@假如

公共LoginRetrofitService Provide001(OkHttpClient客户端){

返回一个新的Retrofit.Builder()

.baseUrl(\”https://example.com\”)

。建造()

.create(LoginService.class);

}

}

@单例

@Component(模块=NetworkModule.class)

公共接口应用程序组件{

UserRepository userRepository();

无效注入001(MainActivity活动);

}

@Module 模块以与@Inject 不同的方式提供对象实例。在@Module中,@Provides方法的返回值是依赖实例,参数是进一步依赖的对象。此外,您需要使用@Component 参数来应用该模块。

到目前为止我们构建的依赖关系图如下所示:

3.5 @Subcomponent

@Subcomponent:子组件的概念允许您定义更细粒度的范围。

子组件是继承并扩展其父组件的对象图的组件。子组件中的对象可以依赖于父组件提供的对象,但父组件不能依赖于子组件所依赖的对象。 (这是一个简单的包含关系,对吧?)

让我们继续看一个简单的例子。假设您有一个依赖于LoginModel 的登录模块LoginActivity。我们的要求是定义一个子组件,其生命周期仅存在于单次登录过程中。 3.2节提到,Activity不能使用构造函数注入,因此LoginActivity使用@Inject字段注入语法。

@子组件

公共接口登录组件{

无效注入(LoginActivity 活动);

}

然而,以这种方式定义的LoginComponent实际上不能称为特定组件的子组件,因此必须添加额外的声明。

@Module(子组件=LoginComponent.class)

公共类子组件模块{

}

@Component(模块={NetworkModule.class,SubComponentsModule.class})

@单例

公共接口应用程序组件{

UserRepository userRepository();

LoginComponent.Factory LoginComponent();

}

@子组件

公共接口登录组件{

@子组件.工厂

接口工厂{

创建登录组件();

}

无效注入001(LoginActivity活动);

}

这里我们需要定义一个新的模块SubcomponentModule。您还需要在LoginComponent中定义一个子组件Factory,以便ApplicationComponent知道如何创建LoginComponent的实例。

LoginComponent 现在已声明。为了使LoginComponent 保持与LoginActivity 相同的生命周期,您必须在LoginActivity 中创建一个LoginComponent 实例并维护一个引用。

公共类LoginActivity 扩展Activity {

1.维护子组件的引用,保证生命周期相同

登录组件登录组件;

2.@Inject字段注入

@注入

登录视图模型登录视图模型;

@覆盖

protected void onCreate(bundle 保存实例状态) {

super.onCreate(savedInstanceState);

3.创建子组件实例

登录组件=((MyApplication) getApplicationContext())

.appComponent.loginComponent().create();

4、注射

登录组件.注入(this);

……

}

}

步骤4 初始化loginViewModel 字段。这里有一些地方需要特别注意。如果我重复地将LoginViewModel插入LoginActivity的Fragment中,它是一个对象吗?

@子组件

公共接口登录组件{

@子组件.工厂

接口工厂{

创建登录组件();

}

无效注入001(登录活动登录活动);

void Inject002(LoginUsernameFragment 片段);

}

这必须是一个单独的对象,因为我们没有使用3.3 节中描述的@Singleton/@Scope 作用域注释。接下来,添加范围注释。

@范围

@Retention(RetentionPolicy.RUNTIME)

公共@interface活动范围{}

@ActivityScope

@子组件

公私

接口LoginComponent { … }

最后

多年来,编辑了解到,大多数入门级和中级Android工程师想要提高自己,往往必须自己探索和成长,这是不系统的、短暂的,我了解到这并没有帮助。

因此,我们收集整理了《2024年Android移动开发全套学习资料》册。最初的目的也很简单。换句话说,我们想帮助那些想学习、提高自己,但不知道从哪里开始的朋友。

由于文件较多,这里仅展示部分目录截图。这套完整的内容包括对领先制造商的采访、学习笔记、源代码讲义、实践项目、概述路线、电子书和教学视频。它将继续更新。

一个人可以走得快,但一群人可以走得更远。无论是IT行业的资深人士还是对IT行业感兴趣的新人。

欢迎任何人加入我们的圈子(技术交流、学习资源、职场投诉、大公司内部推荐、面试指导),让我们一起学习、成长!

如果您需要此信息,请从第 栏中获取。

因此,我们收集整理了《2024年Android移动开发全套学习资料》册。最初的目的也很简单。换句话说,我们想帮助那些想学习、提高自己,但不知道从哪里开始的朋友。

[外部链接图像正在传输.(img-NSusXASJ-1719166894494)]

[正在传输外部链接图像.(img-RmcTmKgM-1719166894495)]

[外部链接图片正在传输中.(img-Axpbf2r6-1719166894495)]

[外部链接图像正在传输.(img-GFt474aJ-1719166894496)]

由于文件较多,这里仅展示部分目录截图。这套完整的内容包括对领先制造商的采访、学习笔记、源代码讲义、实践项目、概述路线、电子书和教学视频。它将继续更新。

一个人可以走得快,但一群人可以走得更远。无论是IT行业的资深人士还是对IT行业感兴趣的新人。

欢迎任何人加入我们的圈子(技术交流、学习资源、职场投诉、大公司内部推荐、面试指导),让我们一起学习、成长!

如果您需要此信息,请从第 栏中获取。

#上面关于Android_玩转Dagger2到Hilt的依赖注入(一)的相关内容摘自网络,供大家参考。相关信息请参见官方公告。

原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/91870.html

(0)
CSDN的头像CSDN
上一篇 2024年6月24日
下一篇 2024年6月24日

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注