Android——初探Dagger2依赖注入
1,在做项目时,经常需要在一个对象里去创建另一个对象的示例,这种行为是产生耦合的常见形式,对于一个大型项目来说,过多的相互依赖会导致代码难以维护,很容易就会碰到修改一个小需求需要大面积的修改各种代码,如果代码还不是自己写的话,那基本上就是在问候上一个同事的亲人了。再来看看我们今天要了解的Dagger2它的作用是什么,来吧,先来一顿吹比夸奖:依赖注入框架主要用于模块间解耦,提高代码的健壮性和可维护性。Dagger 这个库的取名不仅仅来自它的本意“匕首”,同时也暗示了它的原理。
2,我们来一一个简单的显示场景来引入吧,昨天小弟刚刚到了一个小县城,去拜访了几个老同学,顺便从他们哪儿biang来了一些开公司的钱,搞了个小小的咖啡店CoffeeShop,现在要通过我们的咖啡机CoffeeMachine来生产咖啡,比较嫌我这么穷的穷人已经很少见了,编成也编出不了一个北京的厕所来,必须要来赚外快钱生活了,比较生活是要继续的,嗯,现在我们来生产咖啡。
CoffeeMachine.java
package com.qianmo.daggercoffee.bean; /** * Created by wangjitao on 2016/11/2 0002. * 现实的场景我们有一家咖啡机 * 现在模拟一下咖啡机制造咖啡的过程 */ public class CoffeeMachine { private CoffeeMaker maker; public CoffeeMachine() { maker = new SimpleMaker(); } public String makeCoffee() { return maker.makeCoffee(); } }
这里要先说一下,生产咖啡有很多方式,这里我们为了体验java的多态性,先创建一个CoffeeMaker的的接口,然后通过SimpleMaker就是我们简单的通过咖啡机生产了
CoffeeMaker.java
package com.qianmo.daggercoffee.bean; /** * Created by wangjitao on 2016/11/2 0002. * 这是咖啡制造者,提供一个制造方法 */ public interface CoffeeMaker { String makeCoffee(); }
SimpleMaker.java
package com.qianmo.daggercoffee.bean; /** * Created by wangjitao on 2016/11/2 0002. * 这里创建一个简单的制造方法 */ public class SimpleMaker implements CoffeeMaker { @Override public String makeCoffee() { return "Coffee is made by SimperMarker"; } }
好了,这样我们就可以开开心心的卖我们的咖啡了,时间流逝,由于小镇不是很发达,镇上的唯一一家咖啡店生意日渐火爆,阿呆哥哥笑呵呵的在哪儿坐着数钱,心里面并感叹着“坐着数钱的日子可真爽啊”,可是新的问题也来了,客户觉得咖啡机生产口味太单调了,强烈推荐本哥哥招一名咖啡师,按照客户喜欢的口味来搭配着做咖啡,阿呆哥哥还在IT的菜鸟世界遨游着,毕竟是菜鸟,对于来的需求都是满脸笑容的打赢下来,然后再在之后的通宵加班里面问候产品的亲人。经过公司董事长阿呆哥哥激烈的和各位董事会的股东深刻的分析(就是自己坐在门前自言自语一个多小时),决定斥重资解决这个问题!!!
先来创建我们的咖啡师
Cooker.java
package com.qianmo.daggercoffee.bean; /** * Created by wangjitao on 2016/11/2 0002. * 根据需求添加的咖啡师 */ public class Cooker { private String name; //咖啡师的姓名 private String coffeeKind; //制作咖啡的类型 public Cooker(String name, String coffeeKind) { this.name = name; this.coffeeKind = coffeeKind; } public String make() { return name + " make " + coffeeKind; //咖啡师制作咖啡 } }
这时候我们招来了一个咖啡师,相应的生产的方式SimpleMaker也要变了
SimpleMaker.java
package com.qianmo.daggercoffee.bean; /** * Created by wangjitao on 2016/11/2 0002. * 这里创建一个简单的制造方法 */ public class SimpleMaker implements CoffeeMaker { Cooker cooker ; //添加咖啡师来制作咖啡 public SimpleMaker(Cooker cooker){ this.cooker = cooker ; } @Override public String makeCoffee() { return cooker.make(); } }
相应的我们的咖啡机也要改变一下,现在是专业的咖啡师来操作,不再是阿呆哥哥这个编成的,所以现在要修改成
CoffeeMachine.java
package com.qianmo.daggercoffee.bean; /** * Created by wangjitao on 2016/11/2 0002. * 现实的场景我们有一家咖啡机 * 现在模拟一下咖啡机制造咖啡的过程 */ public class CoffeeMachine { private CoffeeMaker maker; public CoffeeMachine(Cooker cooker) { maker = new SimpleMaker(cooker); } public String makeCoffee() { return maker.makeCoffee(); } }
这时候我们的咖啡机就不乐意了,你升级就升级呗,还要我修改东西,难不成把苏宁的设计师叫来修改一下生产的条件,很明显,这时候以一个程序员的角度,我们这个需求是有问题的。但是客户是上帝啊,不能又不招人吧,怎么办呢,真的是把苏宁的设计师搞过来? 作为在技术部里面敲代码的阿呆哥哥此刻陷入了深深的沉思,是晚上去和产品经理捡肥皂还是直接那键盘去客户那边跪在那里。什么都不管了,先抽一根烟开心开心,不竟陷入了深深的沉思。
这时候作为程序猿的阿呆哥哥就仔细的分析了一波,发现主要是这个构造函数,只要把这个搞定了不就可以了,依赖注入,好嘞,看一下我们下面的代码:
InjectMaker.java
package com.qianmo.daggercoffee.bean; /** * Created by wangjitao on 2016/11/2 0002. */ public interface InjectMaker { void injectMaker(CoffeeMaker maker) ; }
再看看替换CoffeeMachine的CoffeeMachineWithInjection类
CoffeeMachineWithInjection.java
package com.qianmo.daggercoffee.bean; /** * Created by wangjitao on 2016/11/2 0002. */ public class CoffeeMachineWithInjection implements InjectMaker { private CoffeeMaker maker; //常见依赖注入的三种形式 /** * 1,构造函数注入 */ public CoffeeMachineWithInjection(CoffeeMaker maker) { this.maker = maker; } /** * 2,set注入 */ public void setMaker(CoffeeMaker maker) { this.maker = maker; } /** * 3,接口注入 */ @Override public void injectMaker(CoffeeMaker maker) { this.maker = maker; } public String makeCoffee() { return maker.makeCoffee(); } }
这里我们采用第三种方法,接口注入,这样的话我们就完全不怕客户提供需求了。一切都变得如此美好,可以开开心心的找老板加鸡腿了。
ok,在项目中如何防止这样的事情发生呢,很简单,这时候我们得使用Dagger2了!
3,Dagger2的使用
引入:
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.1.0' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' } } apply plugin: 'com.neenbedankt.android-apt' dependencies { apt 'com.google.dagger:dagger-compiler:2.0' compile 'com.google.dagger:dagger:2.0' }
看过Dagger2的人应该都知道,Dagger2是通过注解来实现依赖注入的,所以还没了解过注解的同学可以先去google一下,下面先介绍一下它的四个基本
四个基本
这里说的四个基础,指的是四种基础的注解,他们分别是:
@Inject Inject主要有两个作用,一个是使用在构造函数上,通过标记构造函数让Dagger2来使用(Dagger2通过Inject标记可以在需要这个类实例的时候来找到这个构造函数并把相关实例new出来)从而提供依赖,另一个作用就是标记在需要依赖的变量让Dagger2为其提供依赖。
@Provide 用Provide来标注一个方法,该方法可以在需要提供依赖时被调用,从而把预先提供好的对象当做依赖给标注了@Injection的变量赋值。provide主要用于标注Module里的方法
@Module 用Module标注的类是专门用来提供依赖的。有的人可能有些疑惑,看了上面的@Inject,需要在构造函数上标记才能提供依赖,那么如果我们需要提供的类构造函数无法修改怎么办,比如一些jar包里的类,我们无法修改源码。这时候就需要使用Module了。Module可以给不能修改源码的类提供依赖,当然,能用Inject标注的通过Module也可以提供依赖
@Component Component一般用来标注接口,被标注了Component的接口在编译时会产生相应的类的实例来作为提供依赖方和需要依赖方之间的桥梁,把相关依赖注入到其中。
让我们在代码中来实现一下
package com.qianmo.daggercoffee; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; import com.qianmo.daggercoffee.bean.CoffeeMachine; import com.qianmo.daggercoffee.bean.Cooker; public class MainActivity extends AppCompatActivity implements View.OnClickListener { TextView tvCoffee; Button btnMake; CoffeeMachine coffeeMachine; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tvCoffee = (TextView) findViewById(R.id.tv_coffee); btnMake = (Button) findViewById(R.id.btn_make); btnMake.setOnClickListener(this); Cooker cooker = new Cooker("wangjitao", "Es"); coffeeMachine = new CoffeeMachine(cooker); } private String makeCoffee() { return coffeeMachine.makeCoffee(); } @Override public void onClick(View view) { tvCoffee.setText(makeCoffee()); } }
这是我们以前的写法,使用new对象的方式来获取想要的对象,再看看我们使用Dagger2来怎么注入依赖
首先来分析一下,很明显第一层我们需要一个CoffeeMachine对象,所以在activity中我们要这样写这样的代码
@Inject CoffeeMachine coffeeMachine;
告诉Dagger2我们这里是需要对象的,所以这里需要注入依赖,那下面的问题来了,我们的Dagger2怎么知道它去哪找这个对象呢?
上面提到过,提供依赖可以给相关依赖类的构造函数添加@Inject注解,这样Dagger2就能找到它并用来提供依
先看看原先的CoffeeMachine
package com.qianmo.daggercoffee.bean; /** * Created by wangjitao on 2016/11/2 0002. * 现实的场景我们有一家咖啡机 * 现在模拟一下咖啡机制造咖啡的过程 */ public class CoffeeMachine { private CoffeeMaker maker; public CoffeeMachine(Cooker cooker) { maker = new SimpleMaker(cooker); } public String makeCoffee() { return maker.makeCoffee(); } }
再看看添加了Dagger的代码
package com.qianmo.daggercoffee.bean; import javax.inject.Inject; /** * Created by wangjitao on 2016/11/2 0002. * 现实的场景我们有一家咖啡机 * 现在模拟一下咖啡机制造咖啡的过程 */ public class CoffeeMachine { private CoffeeMaker maker; @Inject public CoffeeMachine(Cooker cooker) { maker = new SimpleMaker(cooker); } public String makeCoffee() { return maker.makeCoffee(); } }
很明显,我们只是在CoffeeMachine的构造函数添加了一个@Inject,这样我们的Dagger2就知道在哪里去拿对象提供者了,但是感觉有点不对啊,我们的构造中好像还new了一个SimpleMaker的对象啊,想我们有强迫症的人是绝对不允许的,所以,先修改CoffeeMachine的代码
package com.qianmo.daggercoffee.bean; import javax.inject.Inject; /** * Created by wangjitao on 2016/11/2 0002. * 现实的场景我们有一家咖啡机 * 现在模拟一下咖啡机制造咖啡的过程 */ public class CoffeeMachine { private CoffeeMaker maker; @Inject public CoffeeMachine(CoffeeMaker maker) { this.maker = maker; } public String makeCoffee() { return maker.makeCoffee(); } }
再来修改SimpleMaker
import javax.inject.Inject; /** * Created by wangjitao on 2016/11/2 0002. * 这里创建一个简单的制造方法 */ public class SimpleMaker implements CoffeeMaker { // /** // * 刚创建的时候的方式 // * @return // */ // @Override // public String makeCoffee() { // return "Coffee is made by SimperMarker"; // } Cooker cooker; //添加咖啡师来制作咖啡 @Inject public SimpleMaker(Cooker cooker) { this.cooker = cooker; } @Override public String makeCoffee() { return cooker.make(); } }
这次变聪明了,SimpleMaker的构造函数中Cooker的依赖也是注入进来的,这个时候我们还需要提供Cooker的依赖
but,先打住一下,我们前面说了,除了构造函数提供依赖,还能用Module提供,所以,机智的我们这次用Module来提供依赖。
这里我们创建一个新的Module
SimpleModule.java
package com.qianmo.daggercoffee.bean; import dagger.Module; import dagger.Provides; /** * Created by Administrator on 2016/11/3 0003. */ @Module public class SimpleModule { @Provides Cooker provideCooker() { return new Cooker("gao", "noma"); } }
好了 ,这里我们把需要依赖的东西都写好了,现在就是怎么让创建好的对象能在需要它的地方出现,来看看下面这个图
根据上面的图可以看到,很明显我们缺少一个注射器,在Dagger2中,注射器就是我们的Component
package com.qianmo.daggercoffee.bean; import com.qianmo.daggercoffee.MainActivity; import dagger.Component; /** * Created by wangjitao on 2016/11/3 0003. */ @Component(modules = SimpleModule.class) public interface SimpleComponent { void inject(MainActivity mainActivity) ; }
用@Component注解这个接口能让Dagger2在需要注入时找到这个工具,同时还告诉Dagger2提供依赖的是SimpleModule这个类
当然,如果需要用这个Module给MainActivity注入我们的CoffeeMachine,还需要一个inject方法,里面传入我们的MainActivity 对象的实例。
ok,最后再在我们的activity中使用以下
package com.qianmo.daggercoffee; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; import com.qianmo.daggercoffee.bean.CoffeeMachine; import com.qianmo.daggercoffee.bean.SimpleComponent; import javax.inject.Inject; public class MainActivity extends AppCompatActivity implements View.OnClickListener { TextView tvCoffee; Button btnMake; @Inject CoffeeMachine coffeeMachine; private SimpleComponent simpleComponent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tvCoffee = (TextView) findViewById(R.id.tv_coffee); btnMake = (Button) findViewById(R.id.btn_make); btnMake.setOnClickListener(this); // Cooker cooker = new Cooker("wangjitao", "Es"); // coffeeMachine = new CoffeeMachine(cooker); simpleComponent = DaggerSimpleComponent.builder().simpleModule(getModule()).build(); simpleComponent.inject(this); } private String makeCoffee() { return coffeeMachine.makeCoffee(); } @Override public void onClick(View view) { tvCoffee.setText(makeCoffee()); } }
ok,这样我们就简单的使用了Dagger2·····