Dagger是最早由Square公司开发的一款开源的依赖注入工具(Dagger1, 版本1.x),现在由Google接手进行开发和维护(Dagger2, 版本2.x)。Dagger2相比于Dagger1有不少改动的地方,效率更高,本文将通过Demo讲解Dagger2的用法。
在Demo的两个分支(without_dagger_branch、with_dagger_branch)上分别通过自定义依赖,以及使用Dagger2进行依赖注入的方式实现了相同的功能,通过两种方式的比较可以加深理解。
Demo利用RecyclerView实现了一个简单的电影列表,效果如下:
首先理解什么是依赖,看without_dagger_branch的代码如下:
public class MainActivity extends AppCompatActivity {
...
private RecyclerViewAdapter viewAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
viewAdapter = new RecyclerViewAdapter(this);
mRecyclerView.setAdapter(viewAdapter);
...
}
...
}
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
...
private ThirdPartyUtility utility;
public RecyclerViewAdapter(Context context) {
this.context = context;
this.utility = new ThirdPartyUtility(context);
}
...
}
在MainActivity里用到了RecyclerViewAdapter类型的变量viewAdapter,其中RecyclerViewAdapter是我们自定义的RecyclerView的Adapter,而RecyclerViewAdapter里又用到了ThirdPartyUtility类型的变量utility,ThirdPartyUtility是第三方开发的工具包。viewAdapter就称为类MainActivity的依赖,而utility称为类RecyclerViewAdapter的依赖。
像上面代码所示的这种方式,在类里面进行用到的变量的初始化,就是传统的初始化依赖的方式。通过依赖注入框架,我们可以将依赖初始化的过程放在Dagger辅助类里面完成,然后注入到MainActivity中,这样一来方便测试,同时可以使得MainActivity里面的代码聚焦于功能逻辑的实现,而不是各种变量繁杂的初始化。
切换到with_dagger_branch分支,我们通过Dagger进行依赖注入实现同样的功能。Dagger依赖注入主要是通过几个注解实现的,包括@Inject, @Module,@Provides,@Component等。
使用Dagger前先配置一下gradle:
根目录gradle:
buildscript {
...
dependencies {
...
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
...
}
App目录下gradle:
dependencies {
...
compile 'com.google.dagger:dagger:2.0.2'
apt 'com.google.dagger:dagger-compiler:2.0.2'
provided 'org.glassfish:javax.annotation:10.0-b28'
...
}
配置完Gradle,sync一下,开始开发。首先使用@Inject指定要注入的变量:
public class MainActivity extends AppCompatActivity {
...
@Inject
RecyclerViewAdapter viewAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
mRecyclerView.setAdapter(viewAdapter);
...
}
通过用@Inject指定要注入的依赖变量viewAdapter,我们无需再通过new的方式进行变量初始化。
接下来修改RecyclerViewAdapter类,给其构造函数添加@Inject注解,告知Dagger利用哪个函数进行依赖viewAdapter的初始化:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
...
@Inject
ThirdPartyUtility thirdPartyUtility;
@Inject
Context context;
@Inject
public RecyclerViewAdapter() {}
...
}
在RecyclerViewAdapter中,我们用到两个变量thirdPartyUtility和context,同理,我们通过添加@Inject注解实现这两个变量的依赖注入。这里有个问题,跟我们自定义的RecyclerViewAdapter类不同,Context类和ThirdPartyUtility不是我们自定义的类,不受我们的控制,所以我们无法也在它们的构造函数中添加@Inject注解来告知Dagger怎样进行初始化。这里就需要用到@Module注解和@Provides注解来应对这种情况。
新定义一个类ModuleClass,如下:
@Module
public class ModuleClass {
private final Context context;
public ModuleClass(Context context) {
this.context = context;
}
@Provides
Context provideContext() {
return context;
}
@Provides
ThirdPartyUtility provideThirdPartyUtility() {
return new ThirdPartyUtility(context);
}
}
给ModuleClass添加@Module注解,并添加两个以provide作为前缀的函数,分别提供第三方类Context和ThirdPartyUtility的初始化。并在两个函数上面添加@Provides注解。这样就可以实现不受我们控制的第三方类的初始化。
最后利用@Component注解添加一个用于连接ModuleClass和MainActivity类的桥梁接口,如下:
@Component(modules = {ModuleClass.class})
public interface ComponentClass {
void inject(MainActivity mainActivity);
}
通过@Component注解指定这是个Component类,并通过modules指定包含的Module类。添加一个inject函数,函数的参数就是要注入的目的类,在这里是MainActivity。
最后就是真正注入依赖的地方,在MainActivity中:
public class MainActivity extends AppCompatActivity {
...
@Inject
RecyclerViewAdapter viewAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
DaggerComponentClass.builder().moduleClass(new ModuleClass(this)).build().inject(this);
mRecyclerView.setAdapter(viewAdapter);
...
}
...
}
这里Dagger框架根据Component类自动生成的一个辅助类DaggerComponentClass,通过这个类进行依赖注入,需要make一下工程生成这个辅助类。这样就完成了依赖viewAdapter的注入。运行代码,得到跟传统的new方式初始化依赖同样的效果。
更多的功能可以通过其他注解实现,如通过@Singleton实现单例,通过@Scope等注解实现作用域的指定等,这里不再介绍,可以参考Google文档进行了解和实践。