前言
本篇文章将结合我自己写的咖啡店卖咖啡的栗子的demo,结合代码来分析一下Dagger2是如何来实现依赖注入的。
在我的第一篇文章中有提到如何配置Dagger2以及Dagger2的几个关键概念,不清楚怎么配置的你,建议可以先看看我的第一篇文章,先配置好Dagger2再开始学习下面的内容,由于结合代码,大部分代码都贴出来的,篇幅较长,做好心理准备,前方预警。
总是想着可以开一家咖啡店,现在现实中开不了,那就现在代码里开一家吧。程序员不仅能宅出一个世界,更重要的你能创建出一个你自己的世界,是不是突然自己好厉害! 好吧,不扯了,回到现实中,好好学习吧。
我先创建一家销售咖啡的咖啡店,然后在里面买各种咖啡,黑咖啡呀等。既然是咖啡店,那肯定要有咖啡呀。所以咖啡就是我需要的一个对象。所以我的咖啡店还需要依赖于咖啡,没咖啡,那怎么行,准备好票子,来买我的咖啡吧。根据这个需求,我的咖啡店类如下:
public class CoffeeShopActivity extends AppCompatActivity {
// 1. 我要卖的咖啡对象,用inject注明要依赖的对象
@Inject
Coffee mCoffee ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
inject();
operate();
}
//2.添加依赖关键代码。
private void inject() {
DaggerCoffeeShopComponent.builder().build().inject(this);
}
private void operate() {
mCoffee.getCoffee();
}
}
Coffee是一个很简单的类,里面提供了一个getCoffee方法,打印了一串信息而已。
public class Coffee {
private static final String TAG = Coffee.class.getSimpleName();
@Inject
public Coffee(){
}
public void getCoffee() {
Log.i(TAG, "black coffee");
}
}
咖啡店里面是个很简单的依赖关系,CoffeeShopActivity依赖于类Coffee,可以看到我们没有用 new
来创建Coffee对象,而是直接通过@Inject来标明CoffeeShopActivity的mCoffee需要依赖注入,来实例化这个变量。是不是很神奇,如果你知道了依赖注入是怎么注入的之后,你就不会觉得很神奇了。下面我们一层层地来剥开这个“洋葱”吧。
首先在上面代码里,成员变量mCoffee定义的时候,添加了@Inject注释,然后Coffee的构造函数添加了@Inject注释。这样就CoffeeShopActivity告诉了Dagger2,我需要Coffee的实例,然后Dagger2就去找到Coffee的构造函数创建一个Coffee。难道就只要这两个类就可以了吗,之前不是说Component是目标类与被注入类之间的桥梁吗? Component呢? 没错,我还没贴出Componet呢。自定义个Componet类如下:
@Component
public interface CoffeeShopComponent {
void inject(CoffeeShopActivity coffeeShopActivity);
}
这个Component是不是很简单,只有一个抽象函数,方法名是inject,方法名不是必须是inject,也可以是其他名字,但是参数一定要是你的目标类,而且注意哦,不能是Activity,一定要是目标类自己的类名。在第一篇文章里面就有提到过,这里再强调一下,Dagger框架是通过参数的类型以及返回值类型,找到要依赖的类,从而建立联系的,所以类名一定要是直接要注入依赖的目标类,返回值也是要注入的类。
好了,注射器(Component)写好了,那要派上用场呀!那是在哪里用注射器把依赖注入了MainActivity呢。请看,我在MainActivity里面第2个注释,inject()方法。没错就是这个方法,在这个方法里面用DaggerCoffeeShopComponent.builder().build().inject(this);
来实现了注入。 等等,这个DaggerCoffeeShopComponet
是哪里来的? 大家还记得在配置Dagger2框架环境的时候,引入了APT的包吗,这个包是一个gradle插件,用于结合注释自动生成代码,所以这个包一定要配置,否则不能自动根据Dagger2的注释来生成关键的代码。想要了解APT的可以看看这篇文章android-apt。 所以DaggerCoffeeShopComponent就是一个自动生成的实现了CoffeeShopComponent的类。文件路径是moudle路径\build\generated\source\apt\debug\com\gotech\changelauncherbg\demodagger\DaggerCoffeeShopComponent
,下面自动生成的类都在这个路径里面。当然APT还根据注释自动生成了其他的代码。我们先分析这个,因为这个是实现依赖注入的入口类。注意啦,自动生成代码,是要你执行了rebuild project
才会自动生成的,所以呢你编写完代码之后记得一定要rebuild
才能得到这些类。
好吧,下面就一起来看看DaggerCoffeeShopComponent是怎么把Coffee的实例,注入到mCoffee里面的。上代码:
//说明这段代码时生成的,并且生成自ComponetProcessor
@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerCoffeeShopComponent implements CoffeeShopComponent {
private MembersInjector coffeeShopActivityMembersInjector;
private DaggerCoffeeShopComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
public static CoffeeShopComponent create() {
return builder().build();
}
// 初始化
private void initialize(final Builder builder) {
this.coffeeShopActivityMembersInjector = CoffeeShopActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), Coffee_Factory.create());
}
//重写inject方法,实现依赖注入
@Override
public void inject(CoffeeShopActivity coffeeShopActivity) {
coffeeShopActivityMembersInjector.injectMembers(coffeeShopActivity);
}
public static final class Builder {
private Builder() {
}
public CoffeeShopComponent build() {
return new DaggerCoffeeShopComponent(this);
}
}
}
从上面的代码可以知道,DaggerCoffeeComponent
用有一个内部建造类Builder,可以看成一个简单的建造模式的应用。利用Builder来建造一个DaggerCoffeeShopComponet
对象然后利用CoffeeShopActivity_MembersInjector
初始化coffeeShopActivityMembersInjector
。这个是真正实现依赖注入的类。从类名MembersInjector
可以看出这是一个用于注入成员变量的注入器,然后要注入的目标类是CoffeeShopActivity
。 接下来,我们看看CoffeeShopActivity_MembersInjector
这个类的代码。
@Generated("dagger.internal.codegen.ComponentProcessor")
public final class CoffeeShopActivity_MembersInjector implements MembersInjector {
private final MembersInjector supertypeInjector;
private final Provider mCoffeeProvider;
public CoffeeShopActivity_MembersInjector(MembersInjector supertypeInjector, Provider mCoffeeProvider) {
assert supertypeInjector != null;
this.supertypeInjector = supertypeInjector;
assert mCoffeeProvider != null;
this.mCoffeeProvider = mCoffeeProvider;
}
@Override
public void injectMembers(CoffeeShopActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
supertypeInjector.injectMembers(instance);
//1.获取一个Coffee的实例,复制给CoffeeShopActivity的实例instance,从这里可以看出mCoffee不能是私有的。
instance.mCoffee = mCoffeeProvider.get();
}
public static MembersInjector create(MembersInjector supertypeInjector, Provider mCoffeeProvider) {
return new CoffeeShopActivity_MembersInjector(supertypeInjector, mCoffeeProvider);
}
}
上面代码注释处,就是实现注入的地方,通过CoffeeProvider
获取一个Coffee
对象,然后复制给CoffeeShopActivity
对象的mCoffee
成员变量。
跟踪下来,终于知道是在什么地方给要依赖的成员变量赋值的了。其实我们可以看出,并不是没有用到new
,只不过Dagger2
通过自动生成代码,帮你new了,你就省去了,这个new的过程,直接可以用了。这个呢,只是一个简单的成员变量的依赖跟踪,其他的通过module来提供依赖对象的例子,其实跟踪路径和这个大同小异。 我这里就以小见大的分析这么个小例子,更复杂的用法如果有什么疑问,欢迎留言交流。
总结
我的咖啡店开好了,咖啡都不用我自己去拿,自然有人给我送过来,真是方便至极。干了这杯咖啡。最后,我做个简单的关键点总结吧!
- 被依赖的成员变量的修饰符,不能是私有的。否则不能实现依赖注入。
- 每次编写完代码之后,记得
rebuild project
工程,自动生成代码。 - 在使用成员变量之前,一定要先注入依赖,即编写一个类似于
CoffeeShopActivity
的inject
方法。注入之后才能使用。 - Component的接口里面的方法,参数类型一定要是目标类,不能是目标类的父类。提供依赖的方法或者构造函数,返回的类型也一定要是 要被依赖的类的类名,不能是父类。
最后呢,虽然只是简单的一个成员变量的依赖注入的流程分析,但是其他复杂的依赖例子,其实看懂了这个,再分析其他的应该也挺简单的,关键是了解了原理。希望大家多多指点,哪里写的不对的,或者文章编写有什么建议的,都欢迎大家留言告知我。喜欢的在文末,轻轻点个赞哦。o 谢谢!Have a nice day!