微信号:guolin_blog

介绍:Android技术分享平台,在这里不仅可以学到各种Android相关的最新技术,还可以将你自己的技术总结分享给其他人,每周定期更新.

Dagger2这次入门就不用放弃了

2016-07-29 07:31 _王剑锋


今天周五了,预祝大家周末愉快。


本篇来自 _王剑锋 的投稿,算的上是一篇入门教程,通过自己的使用总结,阐述了如何快速集成Dagger2以及使用。他还有另外两篇后续博文,感兴趣的朋友可以访问下面的博客地址看一看。


_王剑锋 的博客地址:

http://blog.csdn.net/u012943767


前言


之前也研究过很多次 Dagger2 这东西了,可能以后 RxJava+Retrofit+MVP+Dagger2 是Android发展的主流框架,看了 Dagger2 的实现代码,有点不明所以。


网上也有很多文章介绍 依赖注入Dagger2 等等这些那些的什么组件呀、模块呀、注入呀。但是感觉对于入门来说那些文章都没有说到点子上,具体怎么用?应该怎么写代码?为什么这样写?并没有很明确的说明。我来回看了几遍代码之后,总结出了一点经验,不知道说的对不对。


*没有了解过 Android MVP 结构可能不利于阅读。


为什么使用Dagger2


对于这个问题我也困惑了很久,Java代码就是这样写,并没有考虑过 依赖注入 是什么鬼,并且 依赖注入 有什么好的。下面的链接详细介绍了依赖注入,感兴趣的可以传送过去看看:


http://codethink.me/2015/08/01/dependency-injection-theory


简单来说,依赖注入就是为了 控制反转 解耦 的,这些高深的名词儿可能一时也不懂。不要紧,我举个栗子就能明白了,请看代码:


class A{
}

class B{
   A a;
   public B(){        a = new A();    } }


上面的代码很简单,class B 持有一个 class A 的对象,然后假如根据业务需求需要修改 A类 的某些实现,这样的话就需要修改 B类 中的创建 A对象 的方式。


假想一下,当你的代码规模达到一定的程度的时候,需要改一部分代码,牵一发而动全身,需要改的代码量多,而且容易出错。还有一个不好的情况就是,当要对 A 进行 单元测试 的时候,就要测试 B,这样的耦合可能不是程序员希望看见的。


Dagger2 就是为了解决这样的问题而出现的。这里只是一个简单的例子,可能描述依赖注入的原理不是很清晰,如果不是很了解的话可以从网上搜索出很多文章。


Dagger2的配置


目录添加 apt 支持,apt 是用于自动生成代码来进行依赖注入的。


项目中的 build.gradle 添加:




module build.gradle 添加:




例子


这里通过一个例子来向 Activity 注入一些成员变量(例子代码来自网上)。来说明 Dagger2 的基本使用。


例子使用的是 MVP模式,内容是通过注入一个 Presenter,然后通过 Presenter 来设置 TextView 显示内容为 user.name


其中 User 的代码如下:


public class User {
   public String name;
   public User(String name) {
       this.name = name;    } }


Presenter 的代码:




现在的场景是有一个 DaggerActivity,里面持有一个 DaggerPresenter 的成员,我们该如何使用 Dagger2 来注入这个成员呢?


编写Module


我这里编写了一个 ActivityModule,代码如下:




首先这里编写有一些规则的,类需要用 @Module 注解来标示,可以看到我这个 AcitivtyModule 中定义了一个构造函数,需要传进来一个 DaggerActivity 对象。


我们需要明确一个点,就是 Module 的作用是用来提供生成依赖对象的,比如我要注入 DaggerPresenter,那么这个 Module 的作用就是需要生成一个 DaggerPresenter 的对象,来让 Dagger2 注入到 DaggerActivity 中。


所以我们这里需要编写一个函数 provideDaggerPresenter,这个函数可以从上面的代码看出,我们需要对这个函数使用 @Provides 注解,然后,我们这里需要传入两个参数,一个 DaggerActivity,一个 User 对象。那么,这两个参数从何而来呢?


细心的同学可能会发现,我上面的代码中还定义了两个函数,分别为 provideUser provideActivity,大家猜出点什么没有(嘿嘿),这里 provideDaggerPresenter 的两个参数就是通过这两个函数来获取的。如果没有声明这两个函数的话,可能编译期间会报错哟。通过上述内容,各位同学应该明白了 Module 应该如何编写了吧。


编写 Module 有以下几个注意点:


  • 类需要用 @Module 来标明注解。


  • 这里有一点规则,用 @Provides 注解的函数需要以 provide 开头,然后后面接什么内容都可以,看自己喜欢,事实上,经过我的测试,我把 provideActivity() 改成 provideA() 同样是可以注入成功的,所以大家可以知道,这里是根据返回值类型来标识的,方法名并不重要,只需要保证以 provide 开头即可。


编写ActivityComponent


请看代码:


@Component(modules = ActivityModule.class)
public interface ActivityComponent {
   void inject(DaggerActivity daggerActivity); }


这里的代码够少吧,哈哈,我们编写的这个 Component 需要用 @Component 注解来标识,同时声明了 modules 为上面编写的 ActivityModule,然后提供了一个方法,叫做 inject,用来在 Activity 中注入。(这里为什么要写一个方法叫做inject我暂时还没弄清楚,改名字是可以的,但是参数类型不能改,并且一定要指定modules=ActivityModule才能注入),这里我们暂且理解为提供一个方法来注入对象吧。


Make Project


AndroidStudio -> Build -> Make Project写到这里的时候就可以 Make Project了,完成之后 apt 会自动生成一个 以Dagger开头的Component,比如,我们上面写的是 ActivityComponent ,生成了类名就为 DaggerActivityComponent。这个类我们可以直接使用。


注入Activity中


我们已经生成了一个 DaggerActivityComponent 了,在 Activity onCreated 函数中编写如下代码:


DaggerActivityComponent.builder()
    .activityModule(new ActivityModule(this))
    .build()
    .inject(this);


可以看到我们首先调用这个了类的 builder(),然后调用一些方法。这些方法也有一些规律噢,比如我们的 ActivityComponent 指定的 module ActivityModuleDaggerActivityComponent 就会有一个名为 activityModule 的方法,我们需要调用它,并传入参数,这里我们直接new了一个 ActivityModule 进去。


好了,到此为止,我们已经使用 Dagger2 形成了关联,我们还需要注入 Presenter。在 Activity 中:


@Inject
DaggerPresenter
presenter;


我们直接使用注解 @Inject 就可以对这个成员进行注入了。


下面是我的 Activity 的完整代码:




上面的代码运行起来的结果就是在 DaggerActivity TextView 中显示了一串字符串 "user form ActivityModule",虽然例子简单,但是基本上实现了简单依赖注入,希望对于 Dagger2 的入门有点启发。


进阶


好啦,现在我们的项目又有新需求了,我们希望提供一个全局的 OkHttpClient Retrofit 对象来进行网络请求,他的生命周期是和APP一致的,这个时候我们就需要定制 AppComponent 了。


首先我们按照老规矩,第一步先编写 Module,以下是 ApiModule




请注意,我这里的 provide 方法额外添加了一个 @SingleTon 注解,这里说明是全局单例的对象,而且我这里改动了一小部分代码,把 ActivityModule provideUser 移动到这里来了,我这里是为了演示依赖过程。


接下来编写 AppComponent 了:


@Singleton
@Component(modules = {ApiModule.class})
public interface AppComponent {
   OkHttpClient getClient();
   Retrofit getRetrofit();
   User getUser(); }


这里的 AppComponent 提供了3个方法,分别用来暴露 OkHttpClient、Retrofit和User 对象的,这里暂且不提为什么要暴露,大家别急,继续往下看。


接着就是 Make Project 了,之后就会生成一个叫做 DaggerAppComponent 的类,之后我们在 MyApplicaiotn 中实例化这个 Component


public class MyApplication extends Application {
   AppComponent appComponent;
   
   @Override    public void onCreate() {
       super.onCreate();        appComponent = DaggerAppComponent.builder()                .apiModule(new ApiModule())                .build();    }
   
   public AppComponent getAppComponent() {
       return appComponent;    } }


这里别忘了在 AndroidManifest 中设置为自定义的 MyApplicaiton 哦。上面的代码很简单,我们只是实例化了一个 AppComponent,然后提供了一个方法用于获取这个 Component


然后我们需要修改一下 ActivityComponent,改成下面这样:




@Scope
public @interface ActivityScope { }


改动的地方呢是添加了一个 @ActivityScope 然后,添加了一个 dependencies = AppComponent.class。没错,Component之间也可以依赖的


事实上 @Singleton 中并没有创建单例的能力,那么 AppComponent 中提供的依赖注入是如何实现单例的呢。其实这个原理很简单。


首先 ApiModule 提供了创建实例的方法,接着 AppComponent 中对 ApiModule 进行管理,最后 AppComponent MyApplicaiton 中被实例化了一次。


这个实例化了一次是最重要的呀。仅仅被实例化了一次,那不就是单例么。就是这么简单呀。


可能有些童靴当时就不乐意了,那既然这样都已经实现了单例,那么这个@Singltop 还要来何用?不是多此一举吗。


其实 @Singletop 还有有一些作用的,首先一方面能让你直面的了解到这是一个单例,其次这个 @Singletop 能够更好的管理 Modlue Component 之间的关系。


Dagger2 需要保证 Component Module 是匹配的,就需要用到这个注解。


为什么这样说,我定义了一个 ActivityScope,为什么需要这个 Scope 呢?原因是因为我在 AppComponent 中是有 @Singletop 的,ActivityComponent 中依赖了 AppComponent,所以我们需要使用一个 Scope 来匹配他们之间的关系,不然就会在编译期间报错。(具体 Dagger2 内部是如何保障这个的,并没有深入研究过)。


最后一步啦,改动 DaggerActivity




可以看到我这里添加了两个注入,分别注入了 一个OkHttpClient 一个Retrofit 对象,然后在注入的时候也把 AppComponent 也添加进来了。然后我们先看运行结果,后面我会解释一下整个依赖关系。


Log 输出:




然后在手机上运行的话,TextView 会显示 "name from ApiProvide",从结果看来我们已经成功注入了这3个对象。


这里我们看下 ActivityModule




这里的 provideUser 方法(可以对比第一个例子)已经去掉了,那么根据我前面说的话,那我们需要从哪里获取这个User对象呢?


我们可以看到这个 ActivityComponent 是依赖 AppComponent 的,AppComponent中定义了3个方法:


OkHttpClient getClient();
Retrofit getRetrofit();
User getUser();


分别用来提供这三个对象的,这样就可以解释清楚了,他们存在依赖关系,就像我们对象之间的继承一样,值得注意的是这三个方法也是根据返回值类型来识别的,他们会分别找到 AppComponent 中的 module(也就是 ApiModule)中的 provide 方法来获取对象。


小结


我这里只是对于怎么使用 Dagger2 来了一个流程,并且做出了一些通俗化的解释。听到很多人说这个 Dagger2 入门困难,可能是因为需要理解完 Dagger2 通过APT生成的代码的流程才能完全理解吧。


但是我们通常学习一个框架是学会怎么使用,使用过了之后,才会对它的原理进行了解,然而 Dagger2 的使用起来也并不简单,对于一个没有接触过 Dagger1,又没有了解过 依赖注入 的概念的人来说,一下子需要看明白还是有点难度的。


我也是经历了很多次入门到放弃,感觉自己现在也是理解的不太清晰,其实都是猜的(嘿嘿)。总之这篇文章的着重点是为了让大家知道如何使用 Dagger2,并没有解释过内部的原理,但是希望这些东西能带给一些想入门 Dagger2 又感觉难以理解的人一点点启发吧。






如果你有好的技术文章想和大家分享,欢迎向我的公众号投稿,投稿具体细节请在公众号主页点击“投稿”菜单查看。


欢迎长按下图 -> 识别图中二维码或者扫一扫关注我的公众号:


 
郭霖 更多文章 ActivityThread的main方法究竟做了什么? 福利送上,2016 ChinaJoy一日游 教你一分钟实现动态模糊效果 使用AccessibilityService进行APK自动安装 Android事件分发机制详解
猜您喜欢 一篇文章,读懂Netty的高性能架构之道 需不需要成为全栈工程师 Rails中select使用方法 Neutron社区每周记(6.20~6.26)|MTU 到底能不能愉快的设置了? 10个经典的Java面试题集合