依赖注入(DI)
有些人说Android使用依赖注入是因为很多J2EE的人带来的异域思想, 满天飞的注解让人莫不找头脑, 使简单的行为变得复杂, 表面简化, 实则复杂.

但是在使用其一段时间后, 确实还是挺不错的. 正如其思想之精髓, 让你只关注结果,而忽略制作过程, 呵呵, 跟周星驰他老母恰巧相反.

那么下面就讲讲Android开发中常常的用的一些DI框架, 来简化亲们的开发流程吧.


Butterknife

Butterknite是一个专注于简化View相关使用的DI框架, 而且其不像Spring一样使用反射, 而通过预编译来做到注入效果, 大大降低了消耗, 绿色环保, 下面就来讲讲他的使用, 其实很简单.

配置:
由于目前基本都是用Android Studio开发, 这里添加gradle的依赖就行了

1
compile 'com.jakewharton:butterknife:6.0.0'

使用方法:

  1. 初始化注入
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化
ButterKnife.inject(this);

}
```

2. 指定控件的id, 这里定义了 `TextView` `EditText` `Button` 用于测试
3. 使用`Butterknife`注解简化代码

```Java
//声明注入的View
@InjectView(R.id.search_src_text)
TextView textView;
@InjectView(R.id.edit_query)
EditText editText;
@InjectView(R.id.search)
Button button;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);

//可直接使用啦
textView.setText("Hehe");
}

//也可以注入Listener
@OnClick(R.id.search)
void setEditText() {
editText.setText("Haha");
}
```

这里可以看出, 以往通过`findViewById`以及`setOnClickListener`都得到了简化, 我们**冲上去**使用就行了

类似的还有其他的注解, 如`@OnLongClick` `@OnItemClick` `@OnItemSelected`, 用法类似

当layout复用的时候, 有些不一定存在的View可以通过`@Optional`来标识, 否则会**爆炸**

详细使用可参照 http://jakewharton.github.io/butterknife/

----------

#Dagger
说简单一些, 使用Dagger注入依旧绿色环保, 省去了日常所关注的`构造`过程, 让所有指定在`Moudle`里面的类型, 想用即用.

Dagger的工作原理
![Dagger](/images/dagger.png)

配置:
compile 'com.squareup.dagger:dagger:1.2.2'
compile 'com.squareup.dagger:dagger-compiler:1.2.2'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 使用方法

1.定义Moudle (告诉Dagger其如何构造)
我们首先先定义一个Android服务相关的Moudle, 简化我们对`LocationManager`的使用
```Java AndroidModule
@Module(library = true,
complete = false)
public class AndroidModule {
@Provides
@Singleton
LocationManager provideLocationManager(Application application) {
return (LocationManager) application.getSystemService(LOCATION_SERVICE);
}
}

这里的library = true的意思是可能不会被使用, 这只是一个lib, 默认为false, 如没有被使用会抛错
complete = false的意思是这是一个不完整的moudle, 为何这么说, 可以看出provideLocationManager的参数没有响应提供值的Providers呢, 为什么呢, 因为这个Moudle接下来要被另外一个Moudle引用, 所以application这个参数我们将在下一个Moudle里提供.
@Provides是必须要有的注解, 告诉Dagger这个方法就是提供LocationManager的方法.@Singleton是标注这是一个单例, 保证了线程安全.

DemoMoudle
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
38
@Module(
//声明将要使用注入对象的类
injects = DemoActivity.class,
includes = AndroidModule.class,
library = true
)
public class DemoModule {
private Context application;

public DemoModule(Context application) {
this.application = application;
}

@Provides
@Singleton
Context provideApplicationContext() {
return application;
}

@Provides
@Named("hehe")
DaggerYa provideDaggerYa1(Context context) {
return new DaggerTa(context, "hehe");
}

@Provides
@Named("haha")
DaggerYa provideDaggerYa2(Context context) {
return new DaggerTa(context, "haha");
}

@Provides
DaggerLa providerDaggerLa(Context context,
@Named("hehe")
DaggerYa daggerYa) {
return new DaggerLa(context, daggerYa);
}
}

这个Moudle比较长, 首先使用了include来包含我们之前定义的Moudle.
而且也可以发现,这个Moudle提供了ContextProvides

injects比较重要, 这里指定的class就是那些需要使用注入对象的类

有些同学可能会有同样类型的对象注入的问题, 问题的解决办法就是使用@Named来标识其使用的是哪一个@Provides.

2.将Moudle提供给容器ObjectGraph(把之前定义的各个对象的构造方法交给Dagger来维护, 它将会把其注入到程序中去)
由于Application一个Android程序只有一个, 可以通过getApplication获得,那么最佳存放容器与注入的地方就是这里了,请看代码

DemoApplication
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class DemoApplication extends Application {
private ObjectGraph graph;

@Override public void onCreate() {
super.onCreate();

graph = ObjectGraph.create(getModules().toArray());
}

protected List<DemoModule> getModules() {
return Arrays.asList(new DemoModule(this));
}

public void inject(Object object) {
graph.inject(object);
}
}

3.使用注入对象
劳烦了半天, 终于可以尝尝甜头了, 下面看看使用的办法, 请看代码

DemoActivity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class DemoActivity extends Activity {
@Inject
LocationManager locationManager;
@Inject
@Named("hehe")
DaggerYa daggerYa;
@Inject
DaggerLa daggerLa;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

//注入
((DemoApplication) getApplication()).inject(this);
}
}

使用注入对象的类, 定义的Moudle里面必须要有这个类的声明(injects = xxx.class), 并且需要调用容器的inject方法(如加粗部分)

下面就可以通过@Inject轻松使用无需再构造的对象了, 以后只需要添加injects, 并且调用inject方法, 就可以在其他类继续无构造使用提供过Provides的对象啦


PS:

当然把所有注入对象的构造声明全部放在我们的DemoModule里面也显得有些繁琐, 实际上Dagger也支持标准的Java注入格式. 我们可以直接在自己声明的需要注入的类上面添加注解, 来让Dagger找到它的构造函数. 如:

1
2
3
4
5
6
7
8
9
10
11
@Singleton
class DaggerHehe {
@Inject
DaggerHehe(Context context) {
...
}
@Inject
DaggerHehe(DaggerTa ta) {
...
}
}

在预编译的时候, Dagger就会通过@Inject找到DaggerHehe的构造, 当然DaggerHehe的参数也都需要注入哦. @Singleton同样的将该类声明为一个单例了, 并且还少了之前的@Provider注解, 是不是显得很精简. 赶快去试试吧!


详细可参考: http://square.github.io/dagger/