和很多自主学习的人不一样,我接触Dagger 2 框架的原因是刚进公司的时候导师给安排的学习任务,学习方式是组内培训。

听到这个消息的我,以为是部门的人轮流给我讲课。

后来导师跟我说,组内培训的意思是,我先自己好好学这个框架,然后给组内的所有人搞个培训。

没办法,在网上看了很多相关博客,浪费了不少时间,终于还是学有所得,也记录一下我最近的学习进展。

就不多讲什么历史了,你能看到我这篇博客,想来历史什么的科普你都已经被塞到吐了,还是撸代码学得快。

一 环境配置

在module的build.gradle中添加代码:

  1. dependencies {
  2. ......
  3. //dagger2
  4. implementation 'com.google.dagger:dagger:2.7'
  5. annotationProcessor 'com.google.dagger:dagger-compiler:2.7'
  6. //butterknife
  7. implementation 'com.jakewharton:butterknife:10.0.0'
  8. annotationProcessor 'com.jakewharton:butterknife-compiler:10.0.0'
  9. }

为了后面书写代码简便,将ButterKnife一起配置了。

在Project build.gradle中添加如下代码(这段代码是配置ButterKnife使用的,不配置可能就会报TimeOut的错误):

  1. allprojects {
  2. repositories {
  3. google()
  4. maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
  5. jcenter()
  6. }
  7. }

到这里配置就完成了,ButterKnife可以为我们省下很多代码,因为ButterKnife和Dagger的厂家是一样的,很多地方也是共通的,就不多解释原理,拿来就用。

二 源码分析

     我们先创建一个简单的Tools类:

  1. public class Tools {
  2. @Inject
  3. public Tools(){}
  4. }

不需要任何属性,只用@Inject标记一个空的构造方法,然后我们使用Ctrl+F9进行编译。

Dagger2 是通过标记来公式化编写代码,减轻我们重复编写代码的劳动。

这里提到个词“公式化”,其实很好理解。

比如,你要给你给你喜欢的女孩子表白,先跟你寝室兄弟们排练一百遍表白流程。到了女寝楼下,你清一清嗓子,就有人给你打好了灯光,你叫完女孩的名字,身后就有人放飞了粉红的爱心气球,你刚说完那羞羞的三个字,周围就全是大声呼喊“答应他、答应他......”

女孩十分感动,然后拒绝了你......

咳咳,讲偏了!

总之,公式化的东西就是这样,固定好的流程,你只需要打个特殊的手势,别人就知道该怎么做。为什么?因为流程都是公式化的,固定的,已经跑过千百次了,大家闭着眼睛都能敲出来。在程序里也是这样,不要重复造轮子。你只需要打个标记,就像上面提到的@Inject,剩下的繁琐的任务交给喜欢重复劳动的计算机。

那么,看到@Inject这个标记,计算机又做了什么呢?

编译过后会发现build中多了一个文件(你要问我路径在哪?后面我带你找,听我的,慢慢来!),叫做Tools_Factory类,内容如下:

  1. public enum Tools_Factory implements Factory<Tools> {
  2. INSTANCE;
  3.  
  4. @Override
  5. public Tools get() {
  6. return new Tools();
  7. }
  8.  
  9. public static Factory<Tools> create() {
  10. return INSTANCE;
  11. }
  12. }

这就是AS自动生成的代码。一个工厂类,类如其名,就是个工厂。通过create()方法进行创建,通过get()方法获得new Tools对象。

下面我就贴四大基本组件的代码了,先把框架搭起来:

首先是MainActicity:

  1. public class MainActivity extends AppCompatActivity {
  2.  
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. ButterKnife.bind(this);//ButterKnife绑定Activity
  8.  
  9. }
  10.  
  11. @OnClick({R.id.turn_firstactivity})
  12. public void onViewClicked(View view) {
  13. switch (view.getId()){
  14. case R.id.turn_firstactivity:
  15. startActivity(new Intent(this, FirstActivity.class));
  16. break;
  17. }
  18. }
  19. }

就简简单单一个跳转,跳转到我们接下来要使用的FirstActivity中。

贴一下activity_main这个layout,平时我看博客最讨厌别人不把代码贴完,我当然不会犯这个错。

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:orientation="vertical">
  6. <Button
  7. android:layout_width="match_parent"
  8. android:layout_height="wrap_content"
  9. android:id="@+id/turn_firstactivity"
  10. android:text="四大基本组件"/>
  11. </LinearLayout>

接下来是FirstActivity,也就是我们第一个例子的主要Activitypublic class FirstActivity extends AppCompatActivity

  1. @BindView(R.id.fist_text_1) TextView text1;
  2. @BindView(R.id.fist_text_2) TextView text2;
  3. @Inject Tools tool1;
  4. @Inject Tools tool2;
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.first);
  9. ButterKnife.bind(this);//绑定View和Activity
  10. }
  11. }

代码很好理解,用ButterKnife来直接绑定View和Activity,节省了很多代码,而节省的代码也是前面提到“公式化”的体现。

以及对应的layout的代码。

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:orientation="vertical">
  6.  
  7. <TextView
  8. android:layout_width="match_parent"
  9. android:layout_height="wrap_content"
  10. android:id="@+id/fist_text_1"
  11. />
  12.  
  13. <TextView
  14. android:layout_width="match_parent"
  15. android:layout_height="wrap_content"
  16. android:id="@+id/fist_text_2"
  17. />
  18.  
  19. </LinearLayout>

我们再新建接口FirstComponent:

  1. @Component
  2. public interface FirstComponent {
  3.  
  4. void inject(FirstActivity activity);
  5.  
  6. }

这个方法名inject可以随意改写,但是为了方便后来人阅读,建议你还是按照约定俗成的规矩来写,让后来接手你代码的人少骂你两句。

到这一步,我们需要先Ctrl+F9编译一下了。因为有个文件需要Dagger2自动生成后,我们才能使用。

接下来在FirstActivity中添上代码:

  1. protected void onCreate(Bundle savedInstanceState) {
  2. super.onCreate(savedInstanceState);
  3. setContentView(R.layout.first);
  4. ButterKnife.bind(this);
  5.  
  6. DaggerFirstComponent.builder()
  7. .build()
  8. .inject(this);
  9. text1.setText(tool1.toString());
  10. text2.setText(tool2.toString());
  11. }

这个多的文件就是DaggerFirstComponent.java,可以Ctrl+鼠标左键点击DaggerFirstComponent跳转到这个文件查看。

前面找不到自动生成文件在哪的,可以再点击一下这个像准星一样的图标,自动定位到对应的文件夹。

这个文件就是Dagger+Component的名字组合出来的,实际上也是继承了Component接口对其中的方法进行重写。

DaggerFirstComponent代码如下:

  1. public final class DaggerFirstComponent implements FirstComponent {
  2. private MembersInjector<FirstActivity> firstActivityMembersInjector;
  3.  
  4. private DaggerFirstComponent(Builder builder) {
  5. assert builder != null;
  6. initialize(builder);
  7. }
  8.  
  9. public static Builder builder() {
  10. return new Builder();
  11. }
  12.  
  13. public static FirstComponent create() {
  14. return builder().build();
  15. }
  16.  
  17. @SuppressWarnings("unchecked")
  18. private void initialize(final Builder builder) {
  19.  
  20. this.firstActivityMembersInjector =
  21. FirstActivity_MembersInjector.create(Tools_Factory.create());
  22. }
  23.  
  24. @Override
  25. public void inject(FirstActivity activity) {
  26. firstActivityMembersInjector.injectMembers(activity);
  27. }
  28.  
  29. public static final class Builder {
  30. private Builder() {}
  31.  
  32. public FirstComponent build() {
  33. return new DaggerFirstComponent(this);
  34. }
  35. }
  36. }

很明显的Builder建造者模式。

再反观一下Activity中对DaggerFirstComponent的调用

  1. DaggerFirstComponent.builder()
  2. .build()
  3. .inject(this);

首先在里面先初始化一个Builder类,然后调用Builder类中的build()方法对DaggerFirstComponent进行构造。

DaggerFirstComponent构造函数中,有个initialize的初始化函数,给DaggerFirstComponent持有的一个内部成员firstActivityMembersInjector赋值。

注意这行代码:

  1. this.firstActivityMembersInjector = FirstActivity_MembersInjector.create(Tools_Factory.create());

这又涉及到一个关键的文件,记住后缀MembersInjector就行了。

可能有的小伙伴就要问,为啥我不一次性把所有的自动生成文件都列出来?

按照逻辑一步步来,这样比较好理解。

这也是最后一个了:一个工厂类Factory、一个接口实现类DaggerComponent、加上组装类MemberInjector(如果你看见第四个,可能是ButterKnife生成的,有兴趣可以看看)

为啥要把MemberInject叫做组装类,听我慢慢讲来。

MemberInject.java源代码太长了,先只看create()

  1. private final Provider<Tools> tool1AndTool2Provider;
  2.  
  3. public FirstActivity_MembersInjector(Provider<Tools> tool1AndTool2Provider) {
  4. assert tool1AndTool2Provider != null;
  5. this.tool1AndTool2Provider = tool1AndTool2Provider;
  6. }
  7.  
  8. public static MembersInjector<FirstActivity> create(Provider<Tools> tool1AndTool2Provider) {
  9. return new FirstActivity_MembersInjector(tool1AndTool2Provider);
  10. }

大概意思就是先传进来一个工厂类Factory.create(),前面讲过,工厂类就俩方法:create()和get()。然后把create好的工厂类赋给MemberInject类里的成员变量,大概就是把工厂类构建好,保存起来备用。

到这里我们就已经build()跑完了。捋一捋现在都干了啥,DaggerFirstComponent这个类调用了Factory.create(),然后把它给了MemberInject,自己保存了一个MemberInject的实例。到了这步,其实返回的还是个Component,然后继续执行Component里已有的方法而已。

注意这点,到这里返回的是个Component!划重点,后面要考的。

如果你看代码足够认真可能会注意到,DaggerFirstComponent中有个create()方法,对应的就是builder().build()。意味着依赖注入的代码可以简写成下面这种形式:

  1. DaggerFirstComponent.create().inject(this);

没错,确实可以这样,但强烈不建议这样写。划重点,强烈不建议!

因为不符合开闭原则,原因后面会讲到。

DaggerFirstComponent类继承了FirstComponent接口,重写了inject方法:

  1. @Override
  2. public void inject(FirstActivity activity) {
  3. firstActivityMembersInjector.injectMembers(activity);
  4. }

可以看到Inject方法只是给MemberInject的实例传了个Activity。

MemberInject都被喂了哪些东西?

工厂类create好了送进去,Activity也给它了。

为啥叫它组装类!看代码:

  1. instance.tool1 = tool1Provider.get();

instance是Activity的实例,通过Activity找到之前声明的依赖注入实例tools,然后在工厂类实例tool1Provider中get()一个新的对象赋值给它。

东西都是DaggerFirstComponent给它准备好的,他只负责组装。

到这里,整个依赖注入全部完成。

运行结果如下:

注入两个不同的Tools。

回顾一下,其实逻辑很简单。

1、在需要注入依赖的Activity中,用@Inject标记所注入的依赖类的实例tools

2、在所需呀注入的类Tools的构造函数Tools()上标记@Inject,编译会根据此生成Factory工厂类

3、在接口FirstComponent上标记@Component,并写入Inject方法

4、编译生成DaggerFirstComponent.java

5、调用DaggerFirstComponent方法将工厂类的实例和Activity的实例都交给MemberInjecter

6、MemberInjecter组装完成,依赖成功注入。

等等,四大基本组件怎么只讲了两个?

因为两个就已经能基本完成一个简单的依赖注入了,下一章接着讲工厂类的另一种构成方法。

Dagger2 探索记1——四大基本组件(一)的更多相关文章

  1. Dagger2 探索记2——四大基本组件(二)

    书接上文,先回顾以下前一章写的内容. 内容大概就是在Activity中用@Inject标记一个注入的类,然后在这个类的构造函数上也打个@Inject标记,然后使用@Component来连接两边,完成对 ...

  2. Dagger2 探索记3——两大进阶组件(一)

    今天要讲的时@Scope这个组件.为什么说它是进阶组件,就是因为它基本上没作用,但在理解了基本组件之后又必须用到. Scope的意思是作用域,一般用来标记@Provide方法,将生成的对象单例化.但@ ...

  3. Android四大基本组件介绍与生命周期

    Android四大基本组件介绍与生命周期 Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器 ...

  4. 17.(转) Android之四大基本组件介绍与生命周期

    Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器. 一:了解四大基本组件 Activity ...

  5. Android基础_1 四大基本组件介绍与生命周期

    Android四大基本组件分别是Activity,Service(服务),Content Provider(内容提供者),BroadcastReceiver(广播接收器). 一.四大基本组件 Acti ...

  6. Android四大基本组件

    Android四大基本组件分别是 Activity:整个应用程序的门面,负责与用户进行交互. Service:承担大部分工作. Content Provider内容提供者:负责对外提供数据,并允许需要 ...

  7. android开发3:四大基本组件的介绍与生命周期

    android开发3:四大基本组件的介绍与生命周期 Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver ...

  8. 【转】Android四大基本组件介绍与生命周期

    转自:http://www.cnblogs.com/bravestarrhu/archive/2012/05/02/2479461.html Android四大基本组件分别是Activity,Serv ...

  9. android拾遗——四大基本组件介绍与生命周期

    Android四大基本组件分别是Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器. 一:了解四大基本组件 Activity ...

随机推荐

  1. PL/SQL 调用 JAVA代码

    1.直接在 SQL Developer中写入代码 create or replace and compile java source named "HelloWorld" as p ...

  2. 【记忆化搜索】掷骰子 hpuoj

    B. 掷骰子 单点时限: 2.0 sec 内存限制: 512 MB 骰子,中国传统民间娱乐用来投掷的博具,早在战国时期就已经被发明. 现在给你 n 个骰子,求 n 个骰子掷出点数之和为 a 的概率是多 ...

  3. 如何将自己的代码发布到Maven中央仓库?

    去年在公司做工作流相关业务时,当时使用flowable做引擎,中途涉及到一些业务上的需求,自己整理了一些代码,考虑到开源精神,当时就想着将于公司业务无关的代码抽离出来,放到Maven中央仓库中,以供别 ...

  4. SparkSql学习笔记(包含IDEA编写的本地代码)

    Spark SQL and DataFrame 1.为什么要用Spark Sql 原来我们使用Hive,是将Hive Sql 转换成Map Reduce 然后提交到集群上去执行,大大简化了编写MapR ...

  5. UVA10831题解

    Gerg's Cake Gerg is having a party, and he has invited his friends. p of them have arrived already, ...

  6. Java IO体系之RandomAccessFile浅析

    Java IO体系之RandomAccessFile浅析 一.RandomAccessFile综述: 1.1RandomAccessFile简介 RandomAccessFile是java Io体系中 ...

  7. 从零开始搭建Java开发环境第一篇:Java工程师必备软件大合集

    1.JDK https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 目前主流的JDK版 ...

  8. HDU3068 最长回文 Manacher's Algorithm 马拉车算法 模板

    HDU3068 复习了一下这个算法, 注意数组大小要开两倍大. #include <algorithm> #include <iterator> #include <io ...

  9. 牛客暑假多校 H Prefix sum

    题意: 现在有一个2维矩阵, 初始化为0. 并且这个矩阵是及时更新的. dp[i][j] = dp[i-1][j] + dp[i][j-1]; 现在有2种操作: 0 x y   dp[1][x] += ...

  10. hdu Sumsets

    Farmer John commanded his cows to search for different sets of numbers that sum to a given number. T ...