Dagger2是目前流行的一个依赖注入框架。使用它可以降低我们程序中类与类之间的耦合。类实例的创建,初始化,销毁及相互依赖都交由dagger2来管理。我们只需要专注于类本身的业务逻辑,提高我们编写程序的便利性。

传统MVP案例

MVP是我们项目中经常使用的一个应用框架。Model层负责具体的业务逻辑,View层负责界面的展示,Presenter层负责协调Model层与View层,通过调用Model层的业务方法,更新View层的界面展示。现在假如我们由这样一个需求:要求我们获取一段信息在界面上展示。应用MVP框架,我们可以这样实现:

/*** 定义一个View层的统一接口*/
public interface IView {void updateUI(String text);
}/*** View层,负责界面的展示*/
public class TestActivity extends AppCompatActivity implements IView {TestPresent mPresent;//Present层实例@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_test);mPresent=new TestPresent(this);//创建Present层的实例mPresent.updateUI();//调用Present层的更新UI的方法}@Overridepublic void updateUI(String text) {((TextView)findViewById(R.id.textview)).setText(text);}
}/*** Present类,调用Model层的业务方法,更新View层的界面展示*/
public class TestPresent {IView mView;//View层实例TestModel mModel;//Model层实例//构造方法,传入View层的实例public TestPresent(IView view){this.mView=view;this.mModel=new TestModel();//创建Model层的实例}//更新UI的方法public void updateUI(){mView.updateUI(mModel.getText());//调用Model层是业务逻辑,更新View层的界面展示}
}/*** Model类,实现具体的业务逻辑*/
public class TestModel {public TestModel(){}//返回需要展示的信息public String getText(){return "Dagger2应用实践...";}
}

通过以上代码,我们顺利的通过MVP框架实现了我们的需求,代码模块间分工明确,各模块只负责自己本模块的工作。但是这里有一个问题,模块之间相互依赖,一个模块都要依赖其它的模块才能完成实际的功能。根据单一职责原则,一个内聚性高的类,应该只做自己类本身的工作,在需要使用其它的类时,我们只要调用类实例的方法就可以了,实例的创建,初始化,销毁等工作,则不应该由本类负责。通过依赖注入框架,我们就可以只关注类本身的工作,依赖类的创建,初始化,销毁等工作,统统交给依赖注入框架来处理。

Dagger2案例

Dagger2是通过apt根据注解在编译时生成额外的代码实现依赖注入,所以要使用dagger2,首先我们需要在build.gradle(Project:xxx)中配置apt插件:

dependencies {classpath 'com.android.tools.build:gradle:2.3.3'//添加apt插件classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'}

在build.gradle(Module:app)中添加添加dagger2依赖:

apply plugin: 'com.android.application'
//添加如下代码,应用apt插件
apply plugin: 'com.neenbedankt.android-apt'
...
dependencies {...//dagger2compile 'com.google.dagger:dagger:2.4'apt 'com.google.dagger:dagger-compiler:2.4'//java注解compile 'org.glassfish:javax.annotation:10.0-b28'
}

使用Dagger2注解修改各个类:

/*** 定义一个View层的统一接口* View接口保持不变*/
public interface IView {void updateUI(String text);
}/*** View层,负责界面的展示*/
public class TestActivity extends AppCompatActivity implements IView{//当一个成员变量被@Inject注解修饰,并且它的类型构造函数也被@Inject注解修饰,dagger2就会自动实例化该成员类型,并注入到该成员变量@InjectTestPresent mPresent;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_test);DaggerTestComponent.builder().testModule(new TestModule(this)).build().inject(this);//@Component负责连接起@Inject和@Module注解mPresent.updateUI();}@Overridepublic void updateUI(String text) {((TextView)findViewById(R.id.textview)).setText(text);}
}/*** Present类,调用Model层的业务方法,更新View层的界面展示*/
public class TestPresent {IView mView;@InjectTestModel mModel;//Dagger2遇到@Inject标记的成员属性,就会去查看该成员类的构造函数,如果构造函数也被@Inject标记,则会自动初始化,完成依赖注入。//TestPresent的构造函数也被@Inject注解修饰@Injectpublic TestPresent(IView view){this.mView=view;}public void updateUI(){mView.updateUI(mModel.getText());}
}/*** Model类,实现具体的业务逻辑*/
public class TestModel {//构造函数用@Inject修饰@Injectpublic TestModel(){}public String getText(){return "Dagger2应用实践...";}
}/*** Module类提供那些没有构造函数的类的依赖,如第三方类库,系统类,接口类*/
@Module
public class TestModule {private IView mView;public TestModule(IView iView){this.mView=iView;}//@Provides注解的方法,提供IView类的依赖。@Providespublic IView provideIView(){return this.mView;}
}/***Component必须是一个接口类或者抽象*/
@Component(modules = TestModule.class)
public interface TestComponent {void inject(TestActivity testActivity);
}

总结:

  • @Inject 如果一个成员变量被@Inject注解修饰,并且它的类型构造函数也被@Inject注解修饰,那么dagger2就可以帮我们实例化该成员类型,并注入。例如:TestActivity类中的mPresent以及TestPresent类中的mModel,都属于这种情况。
  • @Module 注解修饰的类负责提供那些没有构造函数的类的依赖,如第三方类库,系统类,接口类。
  • @Provides 注解修饰的方法,必须以provide开头后跟提供的依赖类型。负责提供具体类型的依赖,一个@Module修饰的类可以有多个@Provides修饰的方法。
  • @Component 注解修饰的接口或抽象类,负责在@Inject和@Module之间建立连接,当实例化@Inject注解的类时,遇到没有构造函数的成员变量依赖,则该依赖由@Module修饰的类提供。inject方法用于获取TestActivity的实例。

看到这里小伙伴们大概已经知道如何使用dagger2了。但对于代码为何这样写,dagger2内部是怎么帮我们实现各个类间的依赖注入的还不是很清楚。接下来,我们就进入到dagger2为我们生成类的源码中看看,dagger2分别为我们实现了那些类,这些类之间是如何协作来帮助我们实现依赖注入的。

Dagger2原理分析

DaggerTestComponent.builder().testModule(new TestModule(this)).build().inject(this);//@Component连接起@Inject和@Module的关键

DaggerTestComponent,这是什么鬼,我们好像没有定义过这个类啊,别急,这个dagger2根据@Component注解帮我们生成的类,我们进去看看它具体做了什么工作。

DaggerTestComponent源码:

/**1. dagger2根据@Component注解帮我们生成的类,实现我们定义的TestComponent接口
*/
public final class DaggerTestComponent implements TestComponent {private MembersInjector<TestPresent> testPresentMembersInjector;//负责注入TestPresent中被@Inject注解修饰的的成员变量的依赖。dagger2会为TestPresent生成一个TestPresent_MembersInjector类,专门负责TestPresent中@Inject注解的依赖注入private Provider<IView> provideIViewProvider;//负责注入TestPresent中没有构造函数的成员变量的依赖。dagger2会依据@Component中传入的modules值(示例中为TestModule),为Module类中@Provides修饰的方法(示例中为provideIView)提供一个TestModule_ProvideIViewFactory类private Provider<TestPresent> testPresentProvider;//提供TestActivity中TestPresent类型的依赖private MembersInjector<TestActivity> testActivityMembersInjector;//负责注入TestActivity中@Inject修饰的成员变量private DaggerTestComponent(Builder builder) {assert builder != null;initialize(builder);}public static Builder builder() {return new Builder();}@SuppressWarnings("unchecked")private void initialize(final Builder builder) {this.testPresentMembersInjector =TestPresent_MembersInjector.create(TestModel_Factory.create());//创建TestModel_Factory实例,再根据生成的TestModel_Factory实例创建TestPresent_MembersInjector的实例this.provideIViewProvider = TestModule_ProvideIViewFactory.create(builder.testModule);//通过传入的TestModule实例创建TestModule_ProvideIViewFactory的实例this.testPresentProvider =TestPresent_Factory.create(testPresentMembersInjector, provideIViewProvider);//根据testPresentMembersInjector和provideIViewProvider创建TestPresent_Factory的实例this.testActivityMembersInjector = TestActivity_MembersInjector.create(testPresentProvider);//根据testPresentProvider创建TestActivity_MembersInjector的实例}@Overridepublic void inject(TestActivity testActivity) {testActivityMembersInjector.injectMembers(testActivity);//调用testActivityMembersInjector的injectMembers方法为TestActivity提供依赖注入}public static final class Builder {private TestModule testModule;private Builder() {}public TestComponent build() {if (testModule == null) {throw new IllegalStateException(TestModule.class.getCanonicalName() + " must be set");}return new DaggerTestComponent(this);}//传入TestModule的实例public Builder testModule(TestModule testModule) {this.testModule = Preconditions.checkNotNull(testModule);return this;}}
}

可以看到,dagger2为TestActivity实现依赖注入的步骤大概如下:

  1. 根据@Component注解生成Dagger开头的DaggerXxxComponent类(示例中为DaggerTestComponent类)
  2. 根据@Component注解中传入的modules值(示例中为TestModule.class),在DaggerXxxComponent的静态内部类Builder中生成相应的xxxModule方法,传入Module的实例。
  3. 实现@Component注解接口XxxComponent(示例中为TestComponent)中的inject方法,传入TestActivity的示例。

此时,dagger2已经通过@Component关联起了@Inject和@Module注解。但是具体是怎么注入的我们还不是很清楚,我们看到inject方法中调用testActivityMembersInjector的injectMembers()方法完成了对TestActivity的依赖注入,我们再看TestActivity_MembersInjector的源码:

/*** dagger2根据TestActivity中的@Inject注解生成的成员注入类*/
public final class TestActivity_MembersInjector implements MembersInjector<TestActivity> {private final Provider<TestPresent> mPresentProvider;public TestActivity_MembersInjector(Provider<TestPresent> mPresentProvider) {assert mPresentProvider != null;this.mPresentProvider = mPresentProvider;//将create方法中传入的mPresentProvider赋值mPresentProvider}//mPresentProvider即为DaggerTestComponent中创建的TestPresent_Factory的实例testPresentProviderpublic static MembersInjector<TestActivity> create(Provider<TestPresent> mPresentProvider) {return new TestActivity_MembersInjector(mPresentProvider);}//依赖注入方法,在DaggerTestComponent的inject方法中被调用@Overridepublic void injectMembers(TestActivity instance) {if (instance == null) {throw new NullPointerException("Cannot inject members into a null reference");}instance.mPresent = mPresentProvider.get();//通过mPresentProvider的get方法获取到TestPresent的实例并赋值给TestActivity中的mPresent}public static void injectMPresent(TestActivity instance, Provider<TestPresent> mPresentProvider) {instance.mPresent = mPresentProvider.get();}
}

可以看到,上面代码中是通过mPresentProvider(TestPresent_Factory的实例)的get方法获取到TestPresent的实例并复制给TestActivity中的mPresent,TestPresent_Factory的源码:

/*** dagger2生成的TestPresent工厂类*/
public final class TestPresent_Factory implements Factory<TestPresent> {private final MembersInjector<TestPresent> testPresentMembersInjector;//提供TestPresent中@Inject注解修饰的成员变量的依赖private final Provider<IView> viewProvider;//提供TestPresent中不能被@Inject修饰的成员变量(变量类型没有构造函数或构造函数上无法加@Inject注解)的依赖public TestPresent_Factory(MembersInjector<TestPresent> testPresentMembersInjector, Provider<IView> viewProvider) {assert testPresentMembersInjector != null;this.testPresentMembersInjector = testPresentMembersInjector;assert viewProvider != null;this.viewProvider = viewProvider;}@Overridepublic TestPresent get() {//首先根据传入的viewProvider中的get方法获取到IView实例,创建TestPresent的实例,// 再通过MemberInjectors.injecMembers方法,为创建的TestPreset实例注入@Inject修饰的成员变量(mModule)//然后返回创建的TestPresent实例return MembersInjectors.injectMembers(testPresentMembersInjector, new TestPresent(viewProvider.get()));}//此处传入的testPresentMemberInjector即为DaggerTestComponent中创建的TestPresent_MembersInjector的实例//viewProvider即为DaggerTestComponent中创建的TestModule_ProvideIViewFactory的实例public static Factory<TestPresent> create(MembersInjector<TestPresent> testPresentMembersInjector, Provider<IView> viewProvider) {return new TestPresent_Factory(testPresentMembersInjector, viewProvider);}
}

TestPresent_Factory中通过viewProvider的get方法获取到IView的实例,此处的viewProvider即为DaggerTestComponent中传入的TestModule_ProvideIViewFactory实例:

/*** dagger2为@Module修饰的类中的每个@Provides修饰的方法生成的类*/
public final class TestModule_ProvideIViewFactory implements Factory<IView> {private final TestModule module;public TestModule_ProvideIViewFactory(TestModule module) {assert module != null;this.module = module;}@Overridepublic IView get() {return Preconditions.checkNotNull(module.provideIView(), "Cannot return null from a non-@Nullable @Provides method");//通过传入的TestModule实例的provideIView方法获取IView的实例并返回}//此处的module即为TestActivity中通过DaggerTestComponet.Builder().testModule()方法传入的TestModule实例public static Factory<IView> create(TestModule module) {return new TestModule_ProvideIViewFactory(module);}
}

TestPresent_Factory中通过MembersInjectors中的injectMembers方法,为创建的TestPresent注入@Inject修饰的成员变量(mModel)的依赖。MembersInjectors的源码:

public final class MembersInjectors {public static <T> T injectMembers(MembersInjector<T> membersInjector, T instance) {membersInjector.injectMembers(instance);//membersInjector为DaggerTestComponent中传入的TestPresent_MembersInjector的实例testPresentMembersInjector,实际上调用的是TestPresent_MembersInjector的injectMembers()方法return instance;}}

TestPresent_MembersInjector的源码:

public final class TestPresent_MembersInjector implements MembersInjector<TestPresent> {private final Provider<TestModel> mModelProvider;public TestPresent_MembersInjector(Provider<TestModel> mModelProvider) {assert mModelProvider != null;this.mModelProvider = mModelProvider;}public static MembersInjector<TestPresent> create(Provider<TestModel> mModelProvider) {//mModelProvider为DaggerTestComponent中传入的TestModel_Factory的实例return new TestPresent_MembersInjector(mModelProvider);}@Overridepublic void injectMembers(TestPresent instance) {if (instance == null) {throw new NullPointerException("Cannot inject members into a null reference");}instance.mModel = mModelProvider.get();//通过mModelProvider的get方法获取到TestModel的实例并赋值给TestPresent实例中的mModel。}public static void injectMModel(TestPresent instance, Provider<TestModel> mModelProvider) {instance.mModel = mModelProvider.get();}
}

TestModel_Factory源码:

public enum TestModel_Factory implements Factory<TestModel> {INSTANCE;@Overridepublic TestModel get() {return new TestModel();//返回TestModel的实例}public static Factory<TestModel> create() {return INSTANCE;//返回TestModel_Factory的实例}
}

至此,我们完整分析了Dagger2为我们实现依赖注入的全过程,相信大家对Dagger2为我们实现依赖注入的原理有了一定的了解。有什么不解的欢迎大家在评论区给我留言,我会尽量作答。

Dagger2使用详解相关推荐

  1. Dagger2 使用详解

    简书地址 个人博客地址http://zpayh.xyz/ 前言 Dagger2 是一款使用在Java和Android上的依赖注入的一个类库. 配置信息 使用Android Studio 创建一个新的项 ...

  2. google官方mvp+dagger2架构详解

    原文链接:http://www.jianshu.com/p/01d3c014b0b1 1 前言 前段时间分享了一篇文章:google官方架构MVP解析与实战 ,针对这是对google官方示例架构的一个 ...

  3. Dagger2实战详解以及Hilt的使用

    Dagger2是一个依赖注入(DI)框架.那么什么是依赖注入?其实我们一直在使用依赖注入,只是没有提到这个概念.例如下面最简单的例子: class Car{Engine engine;//1.通过构造 ...

  4. 依赖注入框架Dagger2详解(一),依赖注入和控制反转的深入理解

    IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合.更优良的程序,而Dagger2框架是依赖注入思想践行者的优秀代表. 依赖注入框架Dagger2详解(一), ...

  5. 依赖注入神器:Dagger2详解系列

    依赖注入神器:Dagger2详解系列 序言 Dagger2是啥 Dagger2是啥,Google告诉我们: Dagger is a fully static, compile-time depende ...

  6. Google官方MVP+Dagger2架构 dagger2详解

    前言: dagger2是大家项目中使用比较频繁的一个google开源框架,它旨在解决Android 中的依赖注入, 降低层级之间的耦合性,简化了我们在项目中对象实例化操作: dagger2 在Andr ...

  7. dagger android,Dagger2 系列(三)Dagger2.Android使用详解

    前言 前两篇文章我们介绍了dagger2的使用和基本原理,为了适用于android项目代码特点,更简洁的实现四大组件和Fragment的注入,dagger2团队为我们又进一步的封装,也就是我们要介绍的 ...

  8. Android 8.0学习(32)---Android 8.0源码目录结构详解

    Android 8.0源码目录结构详解 android的移植按如下流程:     (1)android linux 内核的普通驱动移植,让内核可以在目标平台上运行起来.     (2)正确挂载文件系统 ...

  9. 史上最全Android build.gradle配置详解

    Android Studio是采用gradle来构建项目的,gradle是基于groovy语言的,如果只是用它构建普通Android项目的话,是可以不去学groovy的.当我们创建一个Android项 ...

最新文章

  1. RHCE 学习笔记(22) 网络用户
  2. 【控制】《多智能体系统一致性协同演化控制理论与技术》纪良浩老师-第7章-二阶时滞多智能体系统分组一致性
  3. png 微软ppt 透明度_用5个技巧教你做出让人眼前一亮的「PPT章节页」,一看就会...
  4. 清理mysql的sleep链接_Mysql Sleep 链接过多导致 CPU 占用过高的问题
  5. java 调用SAP RFC函数错误信息集锦
  6. Lua 脚本内部执行 Redis 命令
  7. 终于知道什么情况下需要实现.NET Core中的IOptions接口
  8. Redis 6.0 新特性:多线程连环 13 问!
  9. 菜鸟学习笔记:Java提升篇3(容器3——泛型、排序)
  10. 自动化测试--实现一套完全解耦的简单测试框架
  11. python 怎么取对数_重新开始学习Python 第二十八天 Python 数学模块
  12. Linux系统管理员命令:sudo
  13. java excel 导入oracle_java代码导入excel数据至oracle(poi方式)
  14. python如何让输出数据对齐、int类型和字符串都有_Python基础-基本数据类型之数字、字符串...
  15. 记一次hive 报错NoViableAltException(-1@[215:51: ( KW_AS )?])
  16. 新人如何顺利度过试用期,让你受益终身的几个技能
  17. Matlab论文插图绘制模板第82期—箭头图(quiver)
  18. [清新]黄花菜花盛开的季节
  19. Python 不用selenium 带你高效爬取京东商品评论
  20. 计算机暑期学校心得,2017暑期学习心得体会6篇_2017暑期学习心得体会

热门文章

  1. Linux系统下安装串口调试工具
  2. Camera Metadata原理
  3. OpenCV(12)-OpenCV的机器学习
  4. 中兴F607Za设备TTL接线图等资料分享
  5. 巴菲特的答卷:年净利润腰斩,百亿美元“错误”,但这些重仓股收益颇丰
  6. Android Instrumentation源码分析(附Activity启动流程)
  7. c语言 内存映射文件,内存映射文件
  8. 汽车电子技术 PPT
  9. 调用百度人脸识别API
  10. jmeter 使用beanshell 编写脚本