带你解析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

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

OWASP十大安全漏洞解析

OWASP(开放式Web应用程序安全项目)发布的“十大安全漏洞”列表是Web应用程序安全领域的权威指南,它总结了Web应用程序中最常见、最危险的安全隐患。以下是对OWASP十大安全漏洞的详细解析: 1. 注入漏洞(Injection) 描述:攻击者通过在应用程序的输入数据中插入恶意代码,从而控制应用程序的行为。常见的注入类型包括SQL注入、OS命令注入、LDAP注入等。 影响:可能导致数据泄

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

CSP 2023 提高级第一轮 CSP-S 2023初试题 完善程序第二题解析 未完

一、题目阅读 (最大值之和)给定整数序列 a0,⋯,an−1,求该序列所有非空连续子序列的最大值之和。上述参数满足 1≤n≤105 和 1≤ai≤108。 一个序列的非空连续子序列可以用两个下标 ll 和 rr(其中0≤l≤r<n0≤l≤r<n)表示,对应的序列为 al,al+1,⋯,ar​。两个非空连续子序列不同,当且仅当下标不同。 例如,当原序列为 [1,2,1,2] 时,要计算子序列 [

多线程解析报表

假如有这样一个需求,当我们需要解析一个Excel里多个sheet的数据时,可以考虑使用多线程,每个线程解析一个sheet里的数据,等到所有的sheet都解析完之后,程序需要提示解析完成。 Way1 join import java.time.LocalTime;public class Main {public static void main(String[] args) thro

ZooKeeper 中的 Curator 框架解析

Apache ZooKeeper 是一个为分布式应用提供一致性服务的软件。它提供了诸如配置管理、分布式同步、组服务等功能。在使用 ZooKeeper 时,Curator 是一个非常流行的客户端库,它简化了 ZooKeeper 的使用,提供了高级的抽象和丰富的工具。本文将详细介绍 Curator 框架,包括它的设计哲学、核心组件以及如何使用 Curator 来简化 ZooKeeper 的操作。 1

Unity3D自带Mouse Look鼠标视角代码解析。

Unity3D自带Mouse Look鼠标视角代码解析。 代码块 代码块语法遵循标准markdown代码,例如: using UnityEngine;using System.Collections;/// MouseLook rotates the transform based on the mouse delta./// Minimum and Maximum values can

图解TCP三次握手|深度解析|为什么是三次

写在前面 这篇文章我们来讲解析 TCP三次握手。 TCP 报文段 传输控制块TCB:存储了每一个连接中的一些重要信息。比如TCP连接表,指向发送和接收缓冲的指针,指向重传队列的指针,当前的发送和接收序列等等。 我们再来看一下TCP报文段的组成结构 TCP 三次握手 过程 假设有一台客户端,B有一台服务器。最初两端的TCP进程都是处于CLOSED关闭状态,客户端A打开链接,服务器端