android最佳实践8:dagger2框架

dagger2

dagger2是一种依赖注入框架,由square开发,现在google负责维护。dagger2一般配合mvp,在mvp已经解耦的基础上,让解耦变得更彻底,以便于测试及维护。

dagger2与dagger的区别

  • 再也没有使用反射:图的验证、配置和预先设置都在编译的时候执行。
  • 容易调试和可跟踪:完全具体地调用提供和创建的堆栈
  • 更好的性能:谷歌声称他们提高了13%的处理性能
  • 代码混淆:使用派遣方法,就如同自己写的代码一样

当然所有这些很棒的特点都需要付出一个代价,那就是缺乏灵活性,例如:Dagger2没用反射所以没有动态机制。

注解含义

  • @Inject: 通常在需要依赖的地方使用这个注解,标识我们需要的实例。另外在@Provide中,直接返回的实例,需要在类的构造函数添加@Inject

  • @Module: 我们定义一个类,用@Module注解,Modules类里面的方法专门提供依赖。这样Dagger在构造类的实例的时候,就知道从哪里去找到需要的依赖。modules的一个重要特征是它们设计为分区并组合在一起(比如说,在我们的Component中可以有多个组成在一起的modules)。

  • @Provide: 在Modules类中,我们定义的方法是用这个注解。这些方法都是用来提供依赖,生成实例的。

  • @Singlton:常用在@Provide之前,表示提供的依赖是个单例,也就是每次都提供同一个实体。

  • @Component: 我们定义一个接口,用Components注解,我们可以理解为Component就是一个注入器,是@Inject和@Module的桥梁。它的主要作用就是连接这两个部分。 Components可以提供所有定义了的类型的实例,比如:我们必须用@Component注解一个接口然后列出所有的@Modules组成该组件,如果缺失了任何一块都会在编译的时候报错。所有的组件都可以通过它的Modules知道依赖的范围。

  • @Scope: Dagger2可以通过自定义注解限定注解作用域。后面会演示一个例子,这是一个非常强大的特点,因为就如前面说的一样,没 必要让每个对象都去了解如何管理他们的实例。在scope的例子中,我们用自定义的@PerActivity注解一个类,所以这个对象存活时间就和 activity的一样。简单来说就是我们可以定义所有范围的粒度(@PerFragment, @PerUser, 等等)。

  • Qualifier: 当类的类型不足以鉴别一个依赖的时候,我们就可以使用这个注解标示。例如:在Android中,我们会需要不同类型的context,所以我们就可以定义 qualifier注解“@ForApplication”和“@ForActivity”,这样当注入一个context的时候,我们就可以告诉 Dagger我们想要哪种类型的context。

引入工程

在app/build.gradle添加依赖

1
2
3
4
5
6
7
8
9
10
apply plugin:'android-apt'

dependencies {
...
//dragger2
//provided 'org.glassfish:javax.annotation:10.0-b28'
apt 'com.google.dagger:dagger-compiler:2.0.2'
compile 'com.google.dagger:dagger:2.0.2'
...
}

因为Dragger2是基于注解的,它会预先生成一些类文件,所以需要在整个项目的/build.gradle文件中加上apt工具:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
buildscript {
repositories {
jcenter()
}
dependencies {
...
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}

allprojects {
repositories {
jcenter()
}
}

task clean(type: Delete) {
delete rootProject.buildDir
}

demo详解

我们通过使用greenDao的示例来说明,引入dagger2的好处。greenDao是非常流行的管理sqldata的orm,我们希望他以单例的形式存在,生命周期等同于Application。

首先,创建GreenDaoModule.java

GreenDaoModule.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Module
public class GreenDaoModule {

private final BaseApp application;

public GreenDaoModule(BaseApp application){
this.application = application;
}

@Provides
@Singleton
public BaseApp provideApplication(){
return application;
}


@Provides
@Singleton
SQLiteDatabase provideSQLiteDatabase(BaseApp baseApp){
DaoMaster.OpenHelper helper = new DaoMaster.DevOpenHelper(baseApp, "zlot-db", null);
SQLiteDatabase sqlDB = helper.getWritableDatabase();
return sqlDB;
}

@Provides
@Singleton
DaoMaster provideDaoMaster(SQLiteDatabase sqLiteDatabase){
DaoMaster daoMaster = new DaoMaster(sqLiteDatabase);
return daoMaster;
}
@Provides
@Singleton
DaoSession provideDaoSession(DaoMaster daoMaster){
DaoSession daoSession = daoMaster.newSession();
return daoSession;
}
}

在上述代码中,我们提供了BaseApp,SQLiteDatabase,DaoMaster,DaoSession的依赖,均是单例模式。
在目标类注解的时候,会在GreenDaoModule寻找依赖并提供实例。

创建AppComponent.java,提供注射器

我们新建一个interface,添加@Component注解,就提供了一个注射器,桥接@Inject跟@Module。

AppComponent.java

1
2
3
4
5
6
7
8
@Singleton
@Component(modules = {GreenDaoModule.class})
public interface AppComponent {
BaseApp getApplication();

BasePresenter inject(BasePresenter basePresenter);

}

其中BasePresenter是注射目标,也就是说想要在哪个类里面使用依赖注解,必须在component中注射。

比如我们想在BasePresenter这个类里,使用依赖注入DaoSession实例,必须在component里插入。

在Application中交给dagger2来完成初始化

Dagger会处理我们的注解,为components生成实现并重命名加上“Dagger”前缀。

MyApplication.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 初始化依赖加载器
*/
private void initInjector(){
appComponent = DaggerAppComponent
.builder()
.apiModule(new ApiModule())
.greenDaoModule(new GreenDaoModule(this))
.build();
}

public AppComponent getAppComponent(){
return appComponent;
}

我们现在可以利用get方法获取创建的component,然后调用inject()方法将BasePresenter作为参数传进去,这样就完成了绑定BasePresenter依赖。

创建BasePresenter

BasePresenter的构造函数

1
2
3
public BasePresenter(BaseApp baseApp){
baseApp.getAppComponent().inject(this);
}

BasePresenter中使用依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class BasePresenter {

@Inject
DaoSession daoSession;

/**
* 自定义http请求任务
*/
@Inject
public HttpTask httpTask;

public BasePresenter(BaseApp baseApp){
baseApp.getAppComponent().inject(this);
}

}

这样我们新建一个Presenter继承自BasePresenter,执行inject绑定,就直接可以使用Application提供的依赖来实例化对象了,而且这些实例都是singlton模式,生命周期等同于Application。
我们直接可以使用DaoSession来完成sql操作了。

继承Presenter

UserReguPresenter类继承自BasePresenter类,testLog()方法来测试数据库的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class UserReguPresenter  extends BasePresenter{
private UserReguActivity userReguActivity;


public UserReguPresenter(UserReguActivity userReguActivity) {
super((BaseApp) userReguActivity.getApplication());
this.userReguActivity = userReguActivity;
}


public void testLog(){
Logger.Debug("调用了presenter的测试");
//new一个实例
RegularHoldInfo regularHoldInfo = new RegularHoldInfo();
regularHoldInfo.setAmount(100.00);
regularHoldInfo.setCcsId(110);
//执行sql inset操作
daoSession.getRegularHoldInfoDao().insert(regularHoldInfo);
//执行sql query操作
Query query = daoSession.getRegularHoldInfoDao()
.queryBuilder()
.where(RegularHoldInfoDao.Properties.CcsId.eq("110"))
.build();
List<RegularHoldInfo> list = (List<RegularHoldInfo>)query.list();
if (list.size()>0){
Logger.Debug("查询结果:"+list.get(0).getAmount());
}

}

参考文献

泡在网上的日子 详解Dagger2

基于Retrofit2.0+RxJava+Dragger2实现不一样的Android网络构架搭建

Dagger2使用