Android单元测试(6):使用dagger2来做依赖注入

597 查看

注:

  • 1. 代码中的 //<= 表示新加的、修改的等需要重点关注的代码
  • 2. Class#method表示一个类的instance method,比如 LoginPresenter#login 表示 LoginPresenter的login(非静态)方法。

问题

前一篇文章中,我们讲述了依赖注入的概念,以及依赖注入对单元测试极其关键的重要性和必要性。在那篇文章的结尾,我们遇到了一个问题,那就是如果不使用DI框架,而全部采用手工来做DI的话,那么所有的Dependency都需要在最上层的client来生成,这可不是件好事情。继续用我们前面的例子来具体说明一下。

假设有一个登录界面,LoginActivity,他有一个LoginPresenterLoginPresenter用到了UserManagerPasswordValidator,为了让问题变得更明显一点,我们假设UserManager用到SharedPreference(用来存储一些用户的基本设置等)和UserApiService,而UserApiService又需要由Retrofit创建,而Retrofit又用到OkHttpClient(比如说你要自己控制timeout、cache等东西)。

应用DI模式,UserManager的设计如下:

LoginPresenter的设计如下:

在这种情况下,最终的client LoginActivity里面要new一个presenter,需要做的事情如下:

这个也太夸张了,LoginActivity所需要的,不过是一个LoginPresenter而已,然而它却需要知道LoginPresenter的Dependency是什么,LoginPresenter的Dependency的Dependency又是什么,然后new一堆东西出来。而且可以预见的是,这个app的其他地方也需要这里的OkHttpClientRetrofitSharedPreferenceUserManager等等dependency,因此也需要new这些东西出来,造成大量的代码重复,和不必要的object instance生成。然而如前所述,我们又必须用到DI模式,这个怎么办呢?

想想,如果能达到这样的效果,那该有多好:我们只需要在一个类似于dependency工厂的地方统一生产这些dependency,以及这些dependency的dependency。所有需要用到这些Dependency的client都从这个工厂里面去获取。而且更妙的是,一个client(比如说LoginActivity)只需要知道它直接用到的Dependency(LoginPresenter),而不需要知道它的Dependency(LoginPresenter)又用到哪些Dependency(UserManagerPasswordValidator)。系统自动识别出这个依赖关系,从工厂里面把需要的Dependency找到,然后把这个client所需要的Dependency创建出来。

有这样一个东西,帮我们实现这个效果吗?相信聪明的你已经猜到了,回答是肯定的,它就是我们今天要介绍的dagger2。

解药:Dagger2

在dagger2里面,负责生产这些Dependency的统一工厂叫做 Module ,所有的client最终是要从module里面获取Dependency的,然而他们不是直接向module要的,而是有一个专门的“工厂管理员”,负责接收client的要求,然后到Module里面去找到相应的Dependency,提供给client们。这个“工厂管理员”叫做 Component。基本上,这是dagger2里面最重要的两个概念。

下面,我们来看看这两个概念,对应到代码里面,是怎么样的。

生产Dependency的工厂:Module

首先是Module,一个Module对应到代码里面就是一个类,只不过这个类需要用dagger2里面的一个annotation @Module来标注一下,来表示这是一个Module,而不是一个普通的类。我们说Module是生产Dependency的地方,对应到代码里面就是Module里面有很多方法,这些方法做的事情就是创建Dependency。用上面的例子中的Dependency来说明: