带你解析Dagger2

2024-06-24 05:38
文章标签 dagger2 解析

本文主要是介绍带你解析Dagger2,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. Dagger2简介:

Dagger2是Dagger1的分支,由谷歌公司接手开发,目前的版本是2.0。Dagger2是受到AutoValue项目的启发。 刚开始,Dagger2解决问题的基本思想是:利用生成和写的代码混合达到看似所有的产生和提供依赖的代码都是手写的样子。

Dagger2具有以下好处:
1) 依赖的注入和配置独立于组件之外,注入的对象在一个独立、不耦合的地方初始化,这样在改变注入对象时,我们只需要修改对象的实现方法,而不用大改代码库。
2) 依赖可以注入到一个组件中:我们可以注入这些依赖的模拟实现,这样使得测试更加简单。
3) app中的组件不需要知道有关实例创建和生命周期的任何事情,这些由我们的依赖注入框架管理的。
dagger2这样的依赖注入框架对MVP架构来说,是最好的解耦工具,可以进一步降低modle-view-presenter之间的耦合度。

2. 什么是依赖注入

如果在 Class A 中,有 Class B 的实例,则称 Class A 对 Class B 有一个依赖。例如下面类 Human 中用到一个 Father 对象,我们就说类 Human 对类 Father 有一个依赖。

public class Human {...Father father;...public Human() {father = new Father();}
}

那什么又是依赖注入呢,依赖注入就是非自己主动初始化依赖,而通过外部来传入依赖的方式,简单来说就是不使用 new 来创建依赖对象。使用 Dagger2 创建依赖对象,我们就不用手动初始化了。个人认为 Dagger2 和 MVP 架构是比较不错的搭配,Activity 依赖的 Presenter 可以使用该DI框架直接生成,实现解耦,简单的使用方式如下:

public class MainActivity extends BaseActivity {@InjectMainActivityPresenter presenter;...  }

上面这些主要是对DI框架有一个初步全局的了解,下面来看看Dagger2的基本内容。Dagger2 通过注解来生成代码,定义不同的角色,主要的注解有:@Inject、@Module 、@Component 、@Provides 、@Scope 、@SubComponent 等。

3. Dagger2注解解释:
接下来介绍Dagger2中的每一个概念:

@Inject: 通常在需要依赖的地方使用这个注解。换句话说,你用它告诉Dagger这个类或者字段需要依赖注入。这样,Dagger就会构造一个这个类的实例并满足他们的依赖。
@Module: Modules类里面的方法专门提供依赖,所以我们定义一个类,用@Module注解,这样Dagger在构造类的实例的时候,就知道从哪里去找到需要的 依赖。modules的一个重要特征是它们设计为分区并组合在一起(比如说,在我们的app中可以有多个组成在一起的modules)。

@Provide: 在modules中,我们定义的方法是用这个注解,以此来告诉Dagger我们想要构造对象并提供这些依赖。

@Component: Components从根本上来说就是一个注入器,也可以说是@Inject和@Module的桥梁,它的主要作用就是连接这两个部分。 Components可以提供所有定义了的类型的实例,比如:我们必须用@Component注解一个接口然后列出所有的@Modules组成该组件,如 果缺失了任何一块都会在编译的时候报错。所有的组件都可以通过它的modules知道依赖的范围。

@Scope: Scopes可是非常的有用,Dagger2可以通过自定义注解限定注解作用域。后面会演示一个例子,这是一个非常强大的特点,因为就如前面说的一样,没 必要让每个对象都去了解如何管理他们的实例。在scope的例子中,我们用自定义的@PerActivity注解一个类,所以这个对象存活时间就和 activity的一样。简单来说就是我们可以定义所有范围的粒度(@PerFragment, @PerUser, 等等)。

Qualifier: 当类的类型不足以鉴别一个依赖的时候,我们就可以使用这个注解标示。例如:在Android中,我们会需要不同类型的context,所以我们就可以定义 qualifier注解“@ForApplication”和“@ForActivity”,这样当注入一个context的时候,我们就可以告诉 Dagger我们想要哪种类型的context。

4. 如何使用Dagger2

首先还是要在我们的build.gradle文件中如下配置:

apply plugin: 'com.neenbedankt.android-apt'buildscript {repositories {jcenter()}dependencies {
classpath 'com.android.tools.build:gradle:1.5.0'classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'}
}android {...
}dependencies {apt 'com.google.dagger:dagger-compiler:2.0'compile 'com.google.dagger:dagger:2.0'...
}

我们添加了编译和运行库,还有必不可少的apt插件,没有这插件,dagger可能不会正常工作,特别是在Android studio中。

Application Component: 生命周期跟Application一样的组件。可注入到AndroidApplication和BaseActivity中类中。

@Singleton // Constraints this component to one-per-application or unscoped bindings.
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {void inject(BaseActivity baseActivity);//Exposed to sub-graphs.Context context();ThreadExecutor threadExecutor();PostExecutionThread postExecutionThread();UserRepository userRepository();
}

使用了@Singleton注解,使其保证唯一性。也许你会问为什么我要将context和其他成员暴露出去。这正是Dagger中 components工作的重要性质:如果你不想把modules的类型暴露出来,那么你就只能显示地使用它们。在这个例子中,我把这些元素暴露给子图, 如果你把他们删掉,编译的时候就会报错。

Application Module: 这里提供了Application Component里的需要注入的对象。这也是为什么@Provide注解的方法要用@Singleton限定。

@Module
public class ApplicationModule {private final AndroidApplication application;public ApplicationModule(AndroidApplication application) {this.application = application;}@Provides @Singleton Context provideApplicationContext() {return this.application;}@Provides @Singleton Navigator provideNavigator() {return new Navigator();}@Provides @Singleton ThreadExecutor provideThreadExecutor(JobExecutor jobExecutor) {return jobExecutor;}@Provides @Singleton PostExecutionThread providePostExecutionThread(UIThread uiThread) {return uiThread;}@Provides @Singleton UserCache provideUserCache(UserCacheImpl userCache) {return userCache;}@Provides @Singleton UserRepository provideUserRepository(UserDataRepository userDataRepository) {return userDataRepository;}
}

Activity Component: 生命周期跟Activity一样的组件。

@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {//Exposed to sub-graphs.Activity activity();
}

@PerActivity是一个自定义的范围注解,作用是允许对象被记录在正确的组件中,当然这些对象的生命周期应该遵循activity的生命周期。

Activity Module: 在对象图中,这个module把activity暴露给相关联的类。比如在fragment中使用activity的context。

@Module
public class ActivityModule {private final Activity activity;public ActivityModule(Activity activity) {this.activity = activity;}@Provides @PerActivity Activity activity() {return this.activity;}
}

User Component: 继承于ActivityComponent的组件,并用@PerActivity注解。我通常会在注入用户相关的fragment中使用。因为 ActivityModule把activity暴露给图了,所以在任何需要一个activity的context的时候,Dagger都可以提供注入, 没必要再在子modules中定义了。

@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = {ActivityModule.class, UserModule.class})
public interface UserComponent extends ActivityComponent {void inject(UserListFragment userListFragment);void inject(UserDetailsFragment userDetailsFragment);
}

User Module: 提供跟用户相关的实例。基于我们的例子,它可以提供用户用例。

@Module
public class UserModule {@Provides @PerActivity GetUserListUseCase provideGetUserListUseCase(GetUserListUseCaseImpl getUserListUseCase) {return getUserListUseCase;}@Provides @PerActivity GetUserDetailsUseCase provideGetUserDetailsUseCase(GetUserDetailsUseCaseImpl getUserDetailsUseCase) {return getUserDetailsUseCase;}
}

整合:
Dagger给了我们一堆选择用来注入依赖:

构造方法注入:在类的构造方法前面注释@Inject
成员变量注入:在类的成员变量(非私有)前面注释@Inject
函数方法注入:在函数前面注释@Inject

这个顺序是Dagger建议使用的,因为在运行的过程中,总会有一些奇怪的问题甚至是空指针,这也意味着你的依赖在对象创建的时候可能还没有初始化 完成。这在Android的activity或者fragment中使用成员变量注入会经常遇到,因为我们没有在它们的构造方法中使用。

看一下我们是如何在BaseActivity中注入一个成员变量。在这个例子中,我们注入了一个叫Navigator的类,它是我们应用中负责管理导航的类。

public abstract class BaseActivity extends Activity {@Inject Navigator navigator;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);this.getApplicationComponent().inject(this);}protected ApplicationComponent getApplicationComponent() {return ((AndroidApplication)getApplication()).getApplicationComponent();}protected ActivityModule getActivityModule() {return new ActivityModule(this);}
}

Navigator类是成员变量注入的,由ApplicationModule里面@Provide注解显示提供的。最终我们初始化 component然后调用inject()方法注入成员变量。我们通过在Activity的onCreate()方法中调用 getApplicationComponent(),完成这些操作。getApplicationComponent()方法放在这儿是为了复用性,它 的主要作用是为了获取实例化的ApplicationComponent对象。

Fragment的presenter中我们也做了同样的事情,这儿的获取方法有一点不一样,因为问我们使用的是per-activity范围限 定的component。所以我们注入到UserDetailsFragment中的UserComponent其实是驻留在 UserDetailsActivity中的。

private UserComponent userComponent;

我们必须在activity的onCreate()方法中用下面的方式初始化。

private void initializeInjector() {this.userComponent = DaggerUserComponent.builder().applicationComponent(getApplicationComponent()).activityModule(getActivityModule()).build();
}

Dagger会处理我们的注解,为components生成实现并重命名加上“Dagger”前缀。因为这个是一个组合的component,所以在构建 的时候,我们必须把所有的依赖的传进去(components和modules)。现在我们的component已经准备好了,接着为了可以满足 fragment的依赖需求,我们写一个获取方法:

@Override public UserComponent getComponent() {return userComponent;
}

我们现在可以利用get方法获取创建的component,然后调用inject()方法将Fragment作为参数传进去,这样就完成了绑定UserDetailsFragment依赖。

@Override public void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);this.getComponent.inject(this);
}

public interface HasComponent<C> {C getComponent();
}

因此,客户端(例如fragment)可以获取并且使用component(来自activity):

@SuppressWarnings("unchecked")
protected <C> C getComponent(Class<C> componentType) {return componentType.cast(((HasComponent<C>)getActivity()).getComponent());
}

这儿使用了强制转换,不论这个客户端不能获取到能用的component,但是至少很快就会失败。


@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerApplicationComponent implements ApplicationComponent {private Provider<Navigator> provideNavigatorProvider;private MembersInjector<BaseActivity> baseActivityMembersInjector;private DaggerApplicationComponent(Builder builder) {  assert builder != null;initialize(builder);}public static Builder builder() {  return new Builder();}private void initialize(final Builder builder) {  this.provideNavigatorProvider = ScopedProvider.create(ApplicationModule_ProvideNavigatorFactory.create(builder.applicationModule));this.baseActivityMembersInjector = BaseActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), provideNavigatorProvider);}@Overridepublic void inject(BaseActivity baseActivity) {  baseActivityMembersInjector.injectMembers(baseActivity);}public static final class Builder {private ApplicationModule applicationModule;private Builder() {  }public ApplicationComponent build() {  if (applicationModule == null) {throw new IllegalStateException("applicationModule must be set");}return new DaggerApplicationComponent(this);}public Builder applicationModule(ApplicationModule applicationModule) {  if (applicationModule == null) {throw new NullPointerException("applicationModule");}this.applicationModule = applicationModule;return this;}}
}

有两个重点需要注意。第一个:由于我们要将依赖注入到activity中,所以会得到一个注入这个比成员的注入器(由Dagger生成的BaseActivity_MembersInjector):

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class BaseActivity_MembersInjector implements MembersInjector<BaseActivity> {private final MembersInjector<Activity> supertypeInjector;private final Provider<Navigator> navigatorProvider;public BaseActivity_MembersInjector(MembersInjector<Activity> supertypeInjector, Provider<Navigator> navigatorProvider) {  assert supertypeInjector != null;this.supertypeInjector = supertypeInjector;assert navigatorProvider != null;this.navigatorProvider = navigatorProvider;}@Overridepublic void injectMembers(BaseActivity instance) {  if (instance == null) {throw new NullPointerException("Cannot inject members into a null reference");}supertypeInjector.injectMembers(instance);instance.navigator = navigatorProvider.get();}public static MembersInjector<BaseActivity> create(MembersInjector<Activity> supertypeInjector, Provider<Navigator> navigatorProvider) {  return new BaseActivity_MembersInjector(supertypeInjector, navigatorProvider);}
}

这个注入器一般都会为所有activity的注入成员提供依赖,只要我们一调用inject()方法,就可以获取需要的字段和依赖。
第二个重点:关于我们的DaggerApplicationComponent类,我们有一个Provider,它不仅仅是一个提供实例的接口,它还是被ScopedProvider构造出来的,可以记录创建实例的范围。
Dagger还会为我们的Navigator类生成一个名叫ApplicationModule_ProvideNavigatorFactory的工厂,这个工厂可以传递上面提到的范围参数然后得到这个范围内的类的实例。

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class ApplicationModule_ProvideNavigatorFactory implements Factory<Navigator> {private final ApplicationModule module;public ApplicationModule_ProvideNavigatorFactory(ApplicationModule module) {  assert module != null;this.module = module;}@Overridepublic Navigator get() {  Navigator provided = module.provideNavigator();if (provided == null) {throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");}return provided;}public static Factory<Navigator> create(ApplicationModule module) {  return new ApplicationModule_ProvideNavigatorFactory(module);}
}

这个类非常简单,它代表我们的ApplicationModule(包含@Provide方法)创建了Navigator类。
总之,上面的代码看起来就像是手敲出来的,而且非常好理解,便于调试。其余还有很多可以去探索,你们可以通过调试去看看Dagger如何完成依赖绑定的。

这篇关于带你解析Dagger2的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1089314

相关文章

解析 XML 和 INI

XML 1.TinyXML库 TinyXML是一个C++的XML解析库  使用介绍: https://www.cnblogs.com/mythou/archive/2011/11/27/2265169.html    使用的时候,只要把 tinyxml.h、tinystr.h、tinystr.cpp、tinyxml.cpp、tinyxmlerror.cpp、tinyxmlparser.

tf.split()函数解析

API原型(TensorFlow 1.8.0): tf.split(     value,     num_or_size_splits,     axis=0,     num=None,     name='split' ) 这个函数是用来切割张量的。输入切割的张量和参数,返回切割的结果。  value传入的就是需要切割的张量。  这个函数有两种切割的方式: 以三个维度的张量为例,比如说一

陀螺仪LSM6DSV16X与AI集成(8)----MotionFX库解析空间坐标

陀螺仪LSM6DSV16X与AI集成.8--MotionFX库解析空间坐标 概述视频教学样品申请源码下载开启CRC串口设置开启X-CUBE-MEMS1设置加速度和角速度量程速率选择设置FIFO速率设置FIFO时间戳批处理速率配置过滤链初始化定义MotionFX文件卡尔曼滤波算法主程序执行流程lsm6dsv16x_motion_fx_determin欧拉角简介演示 概述 本文将探讨

【文末附gpt升级秘笈】腾讯元宝AI搜索解析能力升级:千万字超长文处理的新里程碑

腾讯元宝AI搜索解析能力升级:千万字超长文处理的新里程碑 一、引言 随着人工智能技术的飞速发展,自然语言处理(NLP)和机器学习(ML)在各行各业的应用日益广泛。其中,AI搜索解析能力作为信息检索和知识抽取的核心技术,受到了广泛的关注和研究。腾讯作为互联网行业的领军企业,其在AI领域的探索和创新一直走在前列。近日,腾讯旗下的AI大模型应用——腾讯元宝,迎来了1.1.7版本的升级,新版本在AI搜

消息认证码解析

1. 什么是消息认证码         消息认证码(Message Authentication Code)是一种确认完整性并进行认证的技术,取三个单词的首字母,简称为MAC。         消息认证码的输入包括任意长度的消息和一个发送者与接收者之间共享的密钥,它可以输出固定长度的数据,这个数据称为MAC值。         根据任意长度的消息输出固定长度的数据,这一点和单向散列函数很类似

问题1,PE文件转到内存中出现解析PE不正确的问题

1,使用fopen(FileName, “r”) r的方式读取文件到内存,此时就可能存在问题了,r以只读方式,有时候不表示字符的有可能就不读了,那么内存中就不会是完整的原始文件。所以此时要采用rb,二进制读取的方式。 bool ReadFileToMem(char* FileName, char**buf) { FILE* f; f = fopen(FileName, “rb”); if

[大师C语言(第三十六篇)]C语言信号处理:深入解析与实战

引言 在计算机科学中,信号是一种软件中断,它允许进程之间或进程与内核之间进行通信。信号处理是操作系统中的一个重要概念,它允许程序对各种事件做出响应,例如用户中断、硬件异常和系统调用。C语言作为一门接近硬件的编程语言,提供了强大的信号处理能力。本文将深入探讨C语言信号处理的技术和方法,帮助读者掌握C语言处理信号的高级技巧。 第一部分:C语言信号处理基础 1.1 信号的概念 在Unix-lik

免费内网穿透工具 ,快解析内网穿透解决方案

在IPv4公网IP严重不足的环境下,内网穿透技术越来越多的被人们所使用,使用内网穿透技术的好处有很多。 1:无需公网ip 物以稀为贵,由于可用的公网IP地址越来越少,价格也是水涨船高,一个固定公网IP一年的成本要上万,而使用内网穿透技术则不需要公网IP的支持。 2:提高安全性 使用内网穿透技术,无需在路由器映射端口,我们知道黑客通常会使用端口扫描来寻找攻击对象,不映射端口能大大提高服务器的安全

混合密码系统解析

1. 概述         混合密码系统(hybrid cryptosystem)是将对称密码和非对称密码的优势相结合的方法。一般情况下,将两种不同的方式相结合的做法就称为混合(hybrid)。用混合动力汽车来类比的话,就相当于是一种将发动机(对称密码)和电动机(非对称密码)相结合的系统。         混合密码系统中会先用快速的对称密码来对消息进行加密,这样消息就被转换为了密文从而也就保证

GreenDao 3.0解析

GreenDao 3.0简介: 在Android开发中,数据缓存的开发可能会使用到SQLite,然而在使用它的时候,我们往往要进行许多额外的工作,编写原生的SQL语句与解析查询结果等。所以适应Android的ORM框架就产生了。 目前较为流行的Android的ORM框架就是GreenDao, ORM就是关系映射(Object Relation Mapping),即对象关系映射。它的实现思想就是