dagger2
dagger2 是一种依赖注入框架,由 square 开发,现在 google 负责维护。dagger2 一般配合 mvp ,在 mvp 已经解耦的基础上,让解耦变得更彻底,以便于测试及维护。
注解含义
@Inject:通常程序会将 Dagger2 会将带有此注解的变量或者构造方法参与到依赖注入中去,Dagger2 会实例化这个对象。
@Module:用 Module 标注的类是专门用来提供依赖的。我们开发经常遇到一些 jar 包里的类,我们是无法修改源码的,这时候我们就可以使用 Module 来给这些 jar 包中的类提供依赖,当然,能用 @Inject 标注的依赖生成实例的,用 Module 也可以提供依赖。
@Provide:在 Modules 类中,我们定义的方法是用这个注解。而方法都是用来提供依赖,生成实例的。
@Singlton:单例模式,表示提供的依赖只会被初始化一次。
@Component:用来将 @Inject 和 @Module 联系起来的桥梁,我们可以理解为一个注入器(Injector)负责把目标类所在依赖类的实例注入到目标类中,同时它也管理 Module.
@Scope:它的作用是可以通过自定义注解来限定注解的作用域
@Qualifier:当类的类型不足以鉴别一个依赖的时候,就可以使用这个注解来区分。比如,我们可以定义 @Qualifier 注解 @ForApplication 和 @ForActivity,这样当注入一个 Context的时候,我们就可以让 Dagger 知道我们是需要哪种类型的 Context.
Dagger2 的简单使用示例
导入 Dagger2
1>.在 Project 下的 build.gradle 配置 apt 插件如下代码:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.2'
// 添加android-apt 插件
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
android-apt 是在 Gradle 编译器的插件,根据官方文档介绍,主要有两方面的作用:
- 编译时使用该工具,最终打包时不会将该插件打入到APK中。
- 能根据设置的源路径,在编译时期生成相应的代码。
2>.在 app 下的 build.gradle 中添加依赖及添加应用 apt 插件,如下所示:
apply plugin: 'com.android.application'
// 应用插件
apply plugin: 'com.neenbedankt.android-apt'
......
dependencies {
......
// dagger 2 的配置
compile 'com.google.dagger:dagger:2.4'
apt 'com.google.dagger:dagger-compiler:2.4'
compile 'org.glassfish:javax.annotation:10.0-b28'// 添加java 注解库
}
Dagger2 的一些导入完成,现在我们可以写一个简单的例子来直观的说明该框架的使用核心,我暂且把这个例子分为三个部分,四个步骤来理解。
三个部分:
- 实例化部分:对象的实例化。可以理解为类似容器,将需要的类的实例放在容器里。
- 调用者部分:需要实例化对象的类
- 沟通部分:利用 Dagger2 的一个PI将两者联系起来
四个步骤:
- 编写 Module
- 编写 MainComponent
- AndroidStudio-Build-Make Project 编译项目
- 把 Activity 需要的实例注入 Activity 中
在进行四部曲之前我们首先要编写一个 Car 实体类,如下:
public class Car {
public Car(){
}
public String getBMW() {
return "宝马";
}
}
第一步:编写 Module (实例化部分<容器>)对应的 MainModule 如下代码:
@Module
public class MainModule {
@Provides
Car providerCar(){
return new Car();
}
}
我们来看刚刚建立的 MainModule 类,这个类用了 @Module 注解来标示,我们可以知道的是这个 Module 类的作用是用来提供生成的依赖对象,其次在这个类中有一个返回值为 Car的方法并且用 @Provides 注解标记了,我们需要的实例就是在这个方法中初始化的.
第二步:编写 Component (沟通部分),对应的 MainComponent ,如下:
@Component(modules = MainModule.class)
public interface MainComponent {
//定义注入方法
void inject(MainActivity activity);
}
我们编写的这个 Component 需要用 @Component 注解来标识,同时声明 modules 为上面编写的 MainModule ,然后提供一个方法 inject() ,用来要用该实例地方调用
第三步:AndroidStudio-Build-Rebuild Project 编译项目
我们编译项目之后,apt 会自动生成一个一 Dagger 开头的 Component ,例如:我上面是编写了一个 MainComponent ,那么生成的类名就是为 DaggerMainComponent.
第四步:注入 Acitivity 中(调用者部分)
public class MainActivity extends AppCompatActivity {
@Inject
Car car;
private static final String TAG="lc";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainComponent component=DaggerMainComponent.builder().mainModule(new MainModule()).build();
component.inject(this);
Log.d(TAG,car.getBMW());
}
}
在 Activity 中我们使用了@Inject注解来标识我们需要注入 Activity 的实例(Car),然后我们创建了一个 MainComponent(沟通对象)来连接 MainModule (实例化)与 Activity (调用者),到现在为止我们已经使 Dagger2 形成了关联。运行项目在打日志页面如下:
16:13:57.745 7600-7600/? D/lc: 宝马
打印出了我梦寐以求的宝马车,这样我们在 Activity 中使用 Car 实例就完成了,这里就是使用了 Dagger 的依赖注入来实现的而不是直接 new 一个 Car 对象了。
以上就是一个 dagger2 的使用的一个简单例子,在这个例子的基础上我们修改一下,还可以以另外一种方式来依赖注入。如下代码:
实体类:
public class Car {
@Inject
public Car(){
}
public String getBMW() {
return "奥迪";
}
}
MainModule类:
@Module
public class MainModule {
// @Provides
// Car providerCar(){
// return new Car();
// }
}
首先我们在 Car 类中的构造方法上面加入了 @Inject 的注解,然后再 MainModule 类中把providerCar()方法给注释掉,运行项目,如下打印日志.
17:07:55.576 17699-17699/? D/lc: 奥迪
看到没,只要随便一改宝马瞬间变成了奥迪.也就说明了 MainActivity 中是完成了调用 Car的实例.
这两种方式实现的 Dagger 依赖注入是先后顺序的,逻辑如下:
- 先判断 Module 中是否有提供该对象实例化的方法。
- 如果有则返回,结束。
- 如果没有,则查找该类的构造方法,是否带有@Inject的方法。如果存在则返回。
从这里看出依赖注入有两种方法,第一种就是在 MainModule 类中添加返回一个所需类实例的方法,并且在方法上面添加 @Provides 注解,第二种就是在 MainModule 类中没有添加返回一个所需类实例的方法,然而在实体类中的构造方法上方添加了 @Inject 注解.根据上面的逻辑描述,第一种方法优先级高于第二种方法。
单例注解
上面的例子假如我们 Car 对象注入两次,我们来看他们是不是同一个 Car 对象.
public class MainActivity extends AppCompatActivity {
@Inject
Car car;
@Inject
Car car1;
private static final String TAG="lc";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainComponent component=DaggerMainComponent.builder().mainModule(new MainModule()).build();
component.inject(this);
Log.d(TAG,car.getBMW());
Log.d(TAG,"Car对象地址"+car.toString()+"Car1对象地址"+car1.toString());
}
}
运行项目打印的日志:
08-22 17:36:42.437 10751-10751/? D/lc: 奥迪
08-22 17:36:42.437 10751-10751/? D/lc: Car对象地址---com.zity.dragger2.Car@30b48f3---Car1对象地址com.zity.dragger2.Car@afa49b0
从打印日志可以看出 Car 与 Car 不是同一个对象,也就是在 MainActivity 中创建了两次Car 对象,我们该怎么样使它只创建对象一次呢?如下代码修改,在 MainModule 中:
@Module
public class MainModule {
@Singleton
@Provides
Car providerCar(){
return new Car();
}
}
这里的修改比起之前就是在 providerCar() 方法上面多加了一个 @Singleton 注解标识.
在 MainComponent 中:
@Component(modules = MainModule.class)
@Singleton
public interface MainComponent {
//定义注入方法
void inject(MainActivity activity);
}
这里的修改比起之前就是在j接口上面多加了一个 @Singleton 注解标识.
运行项目,打印日志如下:
08-22 17:40:30.892 14069-14069/com.zity.dragger2 D/lc: 奥迪
08-22 17:40:30.892 14069-14069/com.zity.dragger2 D/lc: Car对象地址---com.zity.dragger2.Car@30b48f3---Car1对象地址com.zity.dragger2.Car@30b48f3
可以看出他们是使用了同一个对象,也就是 Car 对象在 MainActivity 中只创建了一次。
上面的几个例子让我们简单的了解了 Dagger2 的基本用法,以及单例注解的基本使用,下面我们再编写一个例子,更符合我们实际开发中可以借鉴学习的例子。在 github 上开源的关于 dagger2 的项目大多数都是结合 mvp 开发模式降低耦合的使用。
本例子所使用的 MVP 取数据是在 P 层取,然后直接传给 V 层显示数据,这种 MVP 开发模式有别于在 M 层取数据回调给 P 层,然后 P 层把数据再传给给 V 层做数据显示,在正式开发中我就是使用以下这种 MVP 开发模式。
MainActivity 的代码(对应 MVP 的 V 层):
public class MainActivity extends Activity implements MainContract.IView{
@Inject
MainPresenter presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mvp);
// Dagger注入
DaggerMainComponent.builder().mainModule(new MainModule(this)).build().inject(this);
presenter.getData();//获取数据
}
@Override
public void showText(String msg) {
Log.d("lc",msg);
}
实体类代码(对应 MVP 的 M 层):
public class MainDoman {
public MainDoman(){
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name;
}
下面这个类我叫它契约类:
public class MainContract {
interface IView {
void showText(String msg);
}
interface IPresenter {
void getData();
}
}
MainModule 代码:
@Module
public class MainModule {
MainPresenter presenter;
public MainModule(MainActivity activity) {
presenter = new MainPresenter(activity);
}
@Provides
@Singleton
MainPresenter providesMainPresenter() {
return presenter;
}
}
MainComponent 的代码:
@Singleton
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
MainPresenter( MVP 的 P 层)代码:
public class MainPresenter implements MainContract.IPresenter {
MainDoman mainModel;
private MainContract.IView view;
public MainPresenter(MainContract.IView view) {
this.view = view;
mainModel=new MainDoman();
}
@Override
public void getData() {
/*
.......
这里是写去服务器获取数据的代码
这一步就省略了,我下面就直接给M的实例赋值了
*/
mainModel.setName("小强");
view.showText(mainModel.getName());
}
}
Log打印日志:
17:00:32.687 12256-12256/com.zity.dragger2 D/lc: 小强
总结:从 Log 成功打印日志可以得出,在 Activity 注入 Presenter 实例成功,也就是 dagger2 依赖注入跟 MVP 开发模式融合在一起了,也达到了解耦的目的,当然我这些例子使用 dagger2 是最基本最简单的例子,实际开发中会更多的结合 @Scope 与@Qualifier 来使用,如果想了解请 移步该博客;看懂他的博客,你会更深一步的了解并且运用 dagger2 。