Android依赖注入之Dagger应用分析



一、引言

现在 Android 开发越来越多的应用到各种框架,一个好的 APP 架构不但可以支持好的性能,同时也可以更方便快捷的开发功能。依赖注入会帮助你节省数以万计的代码,而对于 Android 来说现在由谷歌 Dagger2 则是不二之选。有些人会说,没用过依赖注入,其实不是的,只要是做 Android 开发,都不可避免使用的,只是自已没注意或不清楚什么是依赖注入。但说真的,在这之前,可能还没使用过 Dagger2,建议你使用,用了之后你就会爱上它。

二、Dagger介绍

Dagger1 最初由 Square 公司推出的,后来 Goole 公司接手,并推出了 Dagger2。

Dagger 是一个依赖注入框架,Butterknife 也是一个依赖注入框架。不过 Butterknife 叫奶油刀,Dagger2 被叫做利器,他的主要作用,就是对象的管理,其目的是为了降低程序耦合。

Dagger优点:

  • 增加开发效率、省去重复的简单体力劳动:首先 New 一个实例的过程是一个重复的简单体力劳动,Dagger2完全可以把 New 一个实例的工作做了,因此我们把主要精力集中在关键业务上、同时也能增加开发效率上。省去写单例的方法,并且也不需要担心自己写的单例方法是否线程安全,自己写的单例是懒汉模式还是饿汉模式。因为 Dagger2 都可以把这些工作做了。

  • 更好的管理类实例:每个 APP 中的 ApplicationComponent 管理整个 APP 的全局类实例,所有的全局类实例都统一交给 ApplicationComponent 管理,并且它们的生命周期与 APP 的生命周期一样。Module 中管理着所对应页面所依赖的所有类实例。

  • 解耦:假如不用 Dagger2 的话,一个类的 New 代码是非常可能充斥在 APP 的多个类中的,假如该类的构造函数发生变化,那这些涉及到的类都得进行修改。用了 Dagger2 后,假如是通过用 Inject 注解标注的构造函数创建类实例,则即使构造函数变的天花乱坠,我们基本上都不需要修改任何代码。假如是通过工厂模式 Module 创建类实例,Module 其实就是把 New 类实例的代码封装起来,这样即使类的构造函数发生变化,只需要修改 Module 即可。

GitHub: Dagger2

三、Dagger注解说明

Dagger2 的基本使用主要是通过各项注解来实现的,所以搞清楚常用注解的作用十分重要

  • @Inject:有两个作用,一是在需要依赖的类(目标类,即宿主)中标记成员变量告诉 Dagger2 这个类型的变量需要一个实例对象。二是标记类中的构造方法(一般为无参构造方法)告诉Dagger2 我可以提供这种类型的依赖实例。

  • @Provides:用来提供依赖实例,对方法进行注解,且都是有返回类型的。用来告诉 Dagger2 我们想如何创建并提供该类型的依赖实例(一般会在方法中new出实例)。用 @Provides 标记的方法,谷歌推荐采用 provide 为前缀,必须用在 @Module 注解的类中,方法所需的参数也需要以方法的形式返回提供。

  • @Module:用来标记类(一般类名以 Module 结尾)。Module 主要的作用是用来集中管理 @Provides 标记的方法。我们定义一个被 @Module 注解的类,Dagger2 就会知道在哪里找到依赖来满足创建类的实例。modules 的一个重要特征是被设计成区块并可以组合在一起供 @Component 所注解的类使用。

  • @Component:用来标记接口或者抽象类(一般以 Component 结尾),是 @Inject(指第一个作用)和 @Module之间的桥梁,主要职责是把二者组合在一起,Module 中的实例对象必须在Component 中暴露出来才能供之后使用。所有的 components 都可以通过它的 modules 知道它所提供的依赖范围。一个 Component 可以依赖一个或多个 Component,并拿到被依赖Component 暴露出来的实例,Component 的 dependencies 属性就是确定依赖关系的实现。

  • @Scope:作用域,Dagger2 通过自定义注解来限定作用域,有一个默认的作用域注解 @Singleton,通常在 Android 中用来标记在 APP 整个生命周期内存活的实例。也可以自定义一个@PerActivity、@PerFragment注解,用来表明实例生命周期与 Activity、Fragment 一致。我们可以自定义作用域的粒度(比如 @PerUser 等等)。

  • @Qualifier:限定符。当一个类的类型不足以标示一个依赖的时候,我们就可以用这个注解。例如,我们有两个 @Provide 注解的方法都需要一个 String 参数,那么在提供依赖的方法上面就可以通过自定义标识“@ForData”或者“@ForImage”来进行区别 Dagger2 里面已经存在一个限定符 @Named 注解,通过 @Named(”xxxxx”)就可以进行标识。具体使用请看下方,十分简单明了。

  • @SubComponent:如果我们需要父组件全部的提供对象,这时我们可以用包含方式而不是用依赖方式,相比于依赖方式,包含方式不需要父组件显式显露对象(依赖方式只能拿到暴露出的实例),就可以拿到父组件全部对象。且 SubComponent 只需要在父 Component 接口中声明就可以了。

四、Dagger使用

4.1 依赖加载

老规矩,在APP的项目gradle中添加依赖:

    compile 'com.google.dagger:dagger:2.x'    //V2.19
    annotationProcessor 'com.google.dagger:dagger-compiler:2.x'     //V2.19

当前最新为V2.19

4.2 创建Moudule

public class A {
    public A(){
        Log.i("dagger","A create!!!");
    }
}
    //第一步 添加@Module 注解
    @Module
    public class MainModule {
        //第二步 使用Provider 注解 实例化对象
        @Provides
        A providerA() {
            return new A();
        }
    }

4.3 创建一个Component

    //第一步 添加@Component
    //第二步 添加module
    @Component(modules = {MainModule.class})
    public interface MainComponent {
        //第三步  写一个方法 绑定Activity /Fragment
        void inject(MainActivity activity);
    }

4.4 Rebuild Project

然后AS 会自动帮我们生成一个Component,开头都是以Dagger开始的。

4.5 将Component与Activity/Fragment绑定关系

    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import com.xxx.Bean.A;
    import com.xxx.component.DaggerMainConponent;
    import javax.inject.Inject;

    public class MainActivity extends AppCompatActivity {
        /***
         * 第二步  使用Inject 注解,获取到A 对象的实例
         */
        @Inject
        A a;

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

            /***
             * 第一步 添加依赖关系
             */
            //第一种方式
            DaggerMainConponent.create().inject(this);

            //第二种方式
            DaggerMainConponent.builder().build().inject(this);

            /***
             * 第三步  调用A 对象的方法
             */
            a.eat();
        }
    }

其过程如下:

  • 创建 Component (桥梁),并调用注入方法
  • 查找当前类中带有 @Inject 的成员变量
  • 根据成员变量的类型从Module中查找哪个有 @Provides 注解的方法返回值为当前类型

4.6 模型变更

public class A {
    private Context mContext;

    public A(Context context){
        mContext = context;
        Log.i("dagger","A create!!!");
    }

}

此时,若使用原始方法,得在每个引用的地方,都去修改。而使用Dagger2,只需修改几处,即可:

@Module   //提供依赖对象的实例
public class MainModule {

    private Context mContext;

    public MainModule(Context context){
        mContext = context;
    }

    @Provides
    Context providesContext(){
        // 提供上下文对象
        return mContext;
    }

    @Provides // 关键字,标明该方法提供依赖对象
    @Singleton
    A providerA(Context context){
        return new A(context);
    }

}

五、总结

我们知道依赖注入主要是用来降低耦合,怎么来降低耦合呢?以上我们是用了一个类的例子,这个类的实例我们也就使用了一次,如果你的项目里有个类使用了上百次,那么你就 New 了上百次的对象,现在突然来了个需求更改,要在类的构造方法里传一个参数,这样的话,你就必须去每一个 New 的地方去修改,想想是不是觉得很给力?


Refer