今天周五了,预祝大家周末愉快。
本篇来自 _王剑锋 的投稿,算的上是一篇入门教程,通过自己的使用总结,阐述了如何快速集成Dagger2以及使用。他还有另外两篇后续博文,感兴趣的朋友可以访问下面的博客地址看一看。
_王剑锋 的博客地址:
http://blog.csdn.net/u012943767
之前也研究过很多次 Dagger2 这东西了,可能以后 RxJava+Retrofit+MVP+Dagger2 是Android发展的主流框架,看了 Dagger2 的实现代码,有点不明所以。
网上也有很多文章介绍 依赖注入、Dagger2 等等这些那些的什么组件呀、模块呀、注入呀。但是感觉对于入门来说那些文章都没有说到点子上,具体怎么用?应该怎么写代码?为什么这样写?并没有很明确的说明。我来回看了几遍代码之后,总结出了一点经验,不知道说的对不对。
*没有了解过 Android MVP 结构可能不利于阅读。
对于这个问题我也困惑了很久,Java代码就是这样写,并没有考虑过 依赖注入 是什么鬼,并且 依赖注入 有什么好的。下面的链接详细介绍了依赖注入,感兴趣的可以传送过去看看:
http://codethink.me/2015/08/01/dependency-injection-theory
简单来说,依赖注入就是为了 控制反转 和 解耦 的,这些高深的名词儿可能一时也不懂。不要紧,我举个栗子就能明白了,请看代码:
class A{
}
class B{
A a;
public B(){
a = new A();
}
}
上面的代码很简单,class B 持有一个 class A 的对象,然后假如根据业务需求需要修改 A类 的某些实现,这样的话就需要修改 B类 中的创建 A对象 的方式。
假想一下,当你的代码规模达到一定的程度的时候,需要改一部分代码,牵一发而动全身,需要改的代码量多,而且容易出错。还有一个不好的情况就是,当要对 A 进行 单元测试 的时候,就要测试 B,这样的耦合可能不是程序员希望看见的。
Dagger2 就是为了解决这样的问题而出现的。这里只是一个简单的例子,可能描述依赖注入的原理不是很清晰,如果不是很了解的话可以从网上搜索出很多文章。
目录添加 apt 支持,apt 是用于自动生成代码来进行依赖注入的。
项目中的 build.gradle 添加:
在 module 的 build.gradle 添加:
这里通过一个例子来向 Activity 注入一些成员变量(例子代码来自网上)。来说明 Dagger2 的基本使用。
例子使用的是 MVP模式,内容是通过注入一个 Presenter,然后通过 Presenter 来设置 TextView 显示内容为 user.name。
其中 User 的代码如下:
public class User {
public String name;
public User(String name) {
this.name = name;
}
}
Presenter 的代码:
现在的场景是有一个 DaggerActivity,里面持有一个 DaggerPresenter 的成员,我们该如何使用 Dagger2 来注入这个成员呢?
编写Module
我这里编写了一个 ActivityModule,代码如下:
首先这里编写有一些规则的,类需要用 @Module 注解来标示,可以看到我这个 AcitivtyModule 中定义了一个构造函数,需要传进来一个 DaggerActivity 对象。
我们需要明确一个点,就是 Module 的作用是用来提供生成依赖对象的,比如我要注入 DaggerPresenter,那么这个 Module 的作用就是需要生成一个 DaggerPresenter 的对象,来让 Dagger2 注入到 DaggerActivity 中。
所以我们这里需要编写一个函数 provideDaggerPresenter,这个函数可以从上面的代码看出,我们需要对这个函数使用 @Provides 注解,然后,我们这里需要传入两个参数,一个 DaggerActivity,一个 User 对象。那么,这两个参数从何而来呢?
细心的同学可能会发现,我上面的代码中还定义了两个函数,分别为 provideUser 和 provideActivity,大家猜出点什么没有(嘿嘿),这里 provideDaggerPresenter 的两个参数就是通过这两个函数来获取的。如果没有声明这两个函数的话,可能编译期间会报错哟。通过上述内容,各位同学应该明白了 Module 应该如何编写了吧。
编写 Module 有以下几个注意点:
类需要用 @Module 来标明注解。
这里有一点规则,用 @Provides 注解的函数需要以 provide 开头,然后后面接什么内容都可以,看自己喜欢,事实上,经过我的测试,我把 provideActivity() 改成 provideA() 同样是可以注入成功的,所以大家可以知道,这里是根据返回值类型来标识的,方法名并不重要,只需要保证以 provide 开头即可。
编写ActivityComponent
请看代码:
@Component(modules = ActivityModule.class)
public interface ActivityComponent {
void inject(DaggerActivity daggerActivity);
}
这里的代码够少吧,哈哈,我们编写的这个 Component 需要用 @Component 注解来标识,同时声明了 modules 为上面编写的 ActivityModule,然后提供了一个方法,叫做 inject,用来在 Activity 中注入。(这里为什么要写一个方法叫做inject我暂时还没弄清楚,改名字是可以的,但是参数类型不能改,并且一定要指定modules=ActivityModule才能注入),这里我们暂且理解为提供一个方法来注入对象吧。
Make Project
注入Activity中
我们已经生成了一个 DaggerActivityComponent 了,在 Activity 的 onCreated 函数中编写如下代码:
DaggerActivityComponent.builder()
.activityModule(new ActivityModule(this))
.build()
.inject(this);
可以看到我们首先调用这个了类的 builder(),然后调用一些方法。这些方法也有一些规律噢,比如我们的 ActivityComponent 指定的 module 是 ActivityModule,DaggerActivityComponent 就会有一个名为 activityModule 的方法,我们需要调用它,并传入参数,这里我们直接new了一个 ActivityModule 进去。
好了,到此为止,我们已经使用 Dagger2 形成了关联,我们还需要注入 Presenter。在 Activity 中:
@Inject
DaggerPresenter presenter;
我们直接使用注解 @Inject 就可以对这个成员进行注入了。
下面是我的 Activity 的完整代码:
上面的代码运行起来的结果就是在 DaggerActivity 的 TextView 中显示了一串字符串 "user form ActivityModule",虽然例子简单,但是基本上实现了简单依赖注入,希望对于 Dagger2 的入门有点启发。
好啦,现在我们的项目又有新需求了,我们希望提供一个全局的 OkHttpClient 和 Retrofit 对象来进行网络请求,他的生命周期是和APP一致的,这个时候我们就需要定制 AppComponent 了。
首先我们按照老规矩,第一步先编写 Module,以下是 ApiModule:
请注意,我这里的 provide 方法额外添加了一个 @SingleTon 注解,这里说明是全局单例的对象,而且我这里改动了一小部分代码,把 ActivityModule 的 provideUser 移动到这里来了,我这里是为了演示依赖过程。
接下来编写 AppComponent 了:
@Singleton
@Component(modules = {ApiModule.class})
public interface AppComponent {
OkHttpClient getClient();
Retrofit getRetrofit();
User getUser();
}
这里的 AppComponent 提供了3个方法,分别用来暴露 OkHttpClient、Retrofit和User 对象的,这里暂且不提为什么要暴露,大家别急,继续往下看。
接着就是 Make Project 了,之后就会生成一个叫做 DaggerAppComponent 的类,之后我们在 MyApplicaiotn 中实例化这个 Component:
public class MyApplication extends Application {
AppComponent appComponent;
@Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder()
.apiModule(new ApiModule())
.build();
}
public AppComponent getAppComponent() {
return appComponent;
}
}
这里别忘了在 AndroidManifest 中设置为自定义的 MyApplicaiton 哦。上面的代码很简单,我们只是实例化了一个 AppComponent,然后提供了一个方法用于获取这个 Component。
然后我们需要修改一下 ActivityComponent,改成下面这样:
@Scope
public @interface ActivityScope {
}
改动的地方呢是添加了一个 @ActivityScope 然后,添加了一个 dependencies = AppComponent.class。没错,Component之间也可以依赖的。
事实上 @Singleton 中并没有创建单例的能力,那么 AppComponent 中提供的依赖注入是如何实现单例的呢。其实这个原理很简单。
首先 ApiModule 提供了创建实例的方法,接着 AppComponent 中对 ApiModule 进行管理,最后 AppComponent 在 MyApplicaiton 中被实例化了一次。
这个实例化了一次是最重要的呀。仅仅被实例化了一次,那不就是单例么。就是这么简单呀。
可能有些童靴当时就不乐意了,那既然这样都已经实现了单例,那么这个@Singltop 还要来何用?不是多此一举吗。
其实 @Singletop 还有有一些作用的,首先一方面能让你直面的了解到这是一个单例,其次这个 @Singletop 能够更好的管理 Modlue 和 Component 之间的关系。
Dagger2 需要保证 Component 和 Module 是匹配的,就需要用到这个注解。
为什么这样说,我定义了一个 ActivityScope,为什么需要这个 Scope 呢?原因是因为我在 AppComponent 中是有 @Singletop 的,ActivityComponent 中依赖了 AppComponent,所以我们需要使用一个 Scope 来匹配他们之间的关系,不然就会在编译期间报错。(具体 Dagger2 内部是如何保障这个的,并没有深入研究过)。
最后一步啦,改动 DaggerActivity:
可以看到我这里添加了两个注入,分别注入了 一个OkHttpClient 和 一个Retrofit 对象,然后在注入的时候也把 AppComponent 也添加进来了。然后我们先看运行结果,后面我会解释一下整个依赖关系。
Log 输出:
然后在手机上运行的话,TextView 会显示 "name from ApiProvide",从结果看来我们已经成功注入了这3个对象。
这里我们看下 ActivityModule:
这里的 provideUser 方法(可以对比第一个例子)已经去掉了,那么根据我前面说的话,那我们需要从哪里获取这个User对象呢?
我们可以看到这个 ActivityComponent 是依赖 AppComponent 的,AppComponent中定义了3个方法:
OkHttpClient getClient();
Retrofit getRetrofit();
User getUser();
分别用来提供这三个对象的,这样就可以解释清楚了,他们存在依赖关系,就像我们对象之间的继承一样,值得注意的是这三个方法也是根据返回值类型来识别的,他们会分别找到 AppComponent 中的 module(也就是 ApiModule)中的 provide 方法来获取对象。
我这里只是对于怎么使用 Dagger2 来了一个流程,并且做出了一些通俗化的解释。听到很多人说这个 Dagger2 入门困难,可能是因为需要理解完 Dagger2 通过APT生成的代码的流程才能完全理解吧。
但是我们通常学习一个框架是学会怎么使用,使用过了之后,才会对它的原理进行了解,然而 Dagger2 的使用起来也并不简单,对于一个没有接触过 Dagger1,又没有了解过 依赖注入 的概念的人来说,一下子需要看明白还是有点难度的。
我也是经历了很多次入门到放弃,感觉自己现在也是理解的不太清晰,其实都是猜的(嘿嘿)。总之这篇文章的着重点是为了让大家知道如何使用 Dagger2,并没有解释过内部的原理,但是希望这些东西能带给一些想入门 Dagger2 又感觉难以理解的人一点点启发吧。
如果你有好的技术文章想和大家分享,欢迎向我的公众号投稿,投稿具体细节请在公众号主页点击“投稿”菜单查看。
欢迎长按下图 -> 识别图中二维码或者扫一扫关注我的公众号: