dagger2框架的学习理解

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_24448923/article/details/77527098

dagger2

dagger2 是一种依赖注入框架,由 square 开发,现在 google 负责维护。dagger2 一般配合 mvp ,在 mvp 已经解耦的基础上,让解耦变得更彻底,以便于测试及维护。

注解含义

@Inject:通常程序会将 Dagger2 会将带有此注解的变量或者构造方法参与到依赖注入中去,Dagger2 会实例化这个对象。

@Module:用 Module 标注的类是专门用来提供依赖的。我们开发经常遇到一些 jar 包里的类,我们是无法修改源码的,这时候我们就可以使用 Module 来给这些 jar 包中的类提供依赖,当然,能用 @Inject 标注的依赖生成实例的,用 Module 也可以提供依赖。

@Provide:在 Modules 类中,我们定义的方法是用这个注解。而方法都是用来提供依赖,生成实例的。

@Singlton:单例模式,表示提供的依赖只会被初始化一次。

@Component:用来将 @Inject 和 @Module 联系起来的桥梁,我们可以理解为一个注入器(Injector)负责把目标类所在依赖类的实例注入到目标类中,同时它也管理 Module.

@Scope:它的作用是可以通过自定义注解来限定注解的作用域

@Qualifier:当类的类型不足以鉴别一个依赖的时候,就可以使用这个注解来区分。比如,我们可以定义 @Qualifier 注解 @ForApplication 和 @ForActivity,这样当注入一个 Context的时候,我们就可以让 Dagger 知道我们是需要哪种类型的 Context.

Dagger2 的简单使用示例

导入 Dagger2

1>.在 Project 下的 build.gradle 配置 apt 插件如下代码:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.2'
        // 添加android-apt 插件
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

    }
}

android-apt 是在 Gradle 编译器的插件,根据官方文档介绍,主要有两方面的作用:

  1. 编译时使用该工具,最终打包时不会将该插件打入到APK中。
  2. 能根据设置的源路径,在编译时期生成相应的代码。

2>.在 app 下的 build.gradle 中添加依赖及添加应用 apt 插件,如下所示:

apply plugin: 'com.android.application'
// 应用插件
apply plugin: 'com.neenbedankt.android-apt'
......
dependencies {
    ......
    // dagger 2 的配置
    compile 'com.google.dagger:dagger:2.4'
    apt 'com.google.dagger:dagger-compiler:2.4'
    compile 'org.glassfish:javax.annotation:10.0-b28'// 添加java 注解库
}

Dagger2 的一些导入完成,现在我们可以写一个简单的例子来直观的说明该框架的使用核心,我暂且把这个例子分为三个部分,四个步骤来理解。

三个部分:

  1. 实例化部分:对象的实例化。可以理解为类似容器,将需要的类的实例放在容器里。
  2. 调用者部分:需要实例化对象的类
  3. 沟通部分:利用 Dagger2 的一个PI将两者联系起来

四个步骤:

  1. 编写 Module
  2. 编写 MainComponent
  3. AndroidStudio-Build-Make Project 编译项目
  4. 把 Activity 需要的实例注入 Activity 中

在进行四部曲之前我们首先要编写一个 Car 实体类,如下:

public class Car {
    public Car(){
    }
    public String getBMW() {
        return "宝马";
    }
}

第一步:编写 Module (实例化部分<容器>)对应的 MainModule 如下代码:

@Module
public class MainModule {
    @Provides
    Car providerCar(){
        return new Car();
    }
}

我们来看刚刚建立的 MainModule 类,这个类用了 @Module 注解来标示,我们可以知道的是这个 Module 类的作用是用来提供生成的依赖对象,其次在这个类中有一个返回值为 Car的方法并且用 @Provides 注解标记了,我们需要的实例就是在这个方法中初始化的.

第二步:编写 Component (沟通部分),对应的 MainComponent ,如下:

@Component(modules = MainModule.class)
public interface MainComponent {
    //定义注入方法
    void inject(MainActivity activity);
}

我们编写的这个 Component 需要用 @Component 注解来标识,同时声明 modules 为上面编写的 MainModule ,然后提供一个方法 inject() ,用来要用该实例地方调用

第三步:AndroidStudio-Build-Rebuild Project 编译项目

我们编译项目之后,apt 会自动生成一个一 Dagger 开头的 Component ,例如:我上面是编写了一个 MainComponent ,那么生成的类名就是为 DaggerMainComponent.

第四步:注入 Acitivity 中(调用者部分)

public class MainActivity extends AppCompatActivity {
    @Inject
    Car car;
    private static final String TAG="lc";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MainComponent component=DaggerMainComponent.builder().mainModule(new MainModule()).build();
        component.inject(this);
        Log.d(TAG,car.getBMW());
    }
}

在 Activity 中我们使用了@Inject注解来标识我们需要注入 Activity 的实例(Car),然后我们创建了一个 MainComponent(沟通对象)来连接 MainModule (实例化)与 Activity (调用者),到现在为止我们已经使 Dagger2 形成了关联。运行项目在打日志页面如下:

16:13:57.745 7600-7600/? D/lc: 宝马

打印出了我梦寐以求的宝马车,这样我们在 Activity 中使用 Car 实例就完成了,这里就是使用了 Dagger 的依赖注入来实现的而不是直接 new 一个 Car 对象了。
以上就是一个 dagger2 的使用的一个简单例子,在这个例子的基础上我们修改一下,还可以以另外一种方式来依赖注入。如下代码:

实体类:

public class Car {
    @Inject
    public Car(){
    }
    public String getBMW() {
        return "奥迪";
    }
}

MainModule类:

@Module
public class MainModule {
//    @Provides
//    Car providerCar(){
//        return new Car();
//    }

}

首先我们在 Car 类中的构造方法上面加入了 @Inject 的注解,然后再 MainModule 类中把providerCar()方法给注释掉,运行项目,如下打印日志.

17:07:55.576 17699-17699/? D/lc: 奥迪

看到没,只要随便一改宝马瞬间变成了奥迪.也就说明了 MainActivity 中是完成了调用 Car的实例.
这两种方式实现的 Dagger 依赖注入是先后顺序的,逻辑如下:

  1. 先判断 Module 中是否有提供该对象实例化的方法。
  2. 如果有则返回,结束。
  3. 如果没有,则查找该类的构造方法,是否带有@Inject的方法。如果存在则返回。

从这里看出依赖注入有两种方法,第一种就是在 MainModule 类中添加返回一个所需类实例的方法,并且在方法上面添加 @Provides 注解,第二种就是在 MainModule 类中没有添加返回一个所需类实例的方法,然而在实体类中的构造方法上方添加了 @Inject 注解.根据上面的逻辑描述,第一种方法优先级高于第二种方法。

单例注解

上面的例子假如我们 Car 对象注入两次,我们来看他们是不是同一个 Car 对象.

public class MainActivity extends AppCompatActivity {
    @Inject
    Car car;
    @Inject
    Car car1;
    private static final String TAG="lc";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MainComponent component=DaggerMainComponent.builder().mainModule(new MainModule()).build();
        component.inject(this);
        Log.d(TAG,car.getBMW());
        Log.d(TAG,"Car对象地址"+car.toString()+"Car1对象地址"+car1.toString());
    }
}

运行项目打印的日志:

08-22 17:36:42.437 10751-10751/? D/lc: 奥迪
08-22 17:36:42.437 10751-10751/? D/lc: Car对象地址---com.zity.dragger2.Car@30b48f3---Car1对象地址com.zity.dragger2.Car@afa49b0

从打印日志可以看出 Car 与 Car 不是同一个对象,也就是在 MainActivity 中创建了两次Car 对象,我们该怎么样使它只创建对象一次呢?如下代码修改,在 MainModule 中:

@Module
public class MainModule {
    @Singleton
    @Provides
    Car providerCar(){
        return new Car();
    }
}

这里的修改比起之前就是在 providerCar() 方法上面多加了一个 @Singleton 注解标识.

在 MainComponent 中:

@Component(modules = MainModule.class)
@Singleton
public interface MainComponent {
    //定义注入方法
    void inject(MainActivity activity);
}

这里的修改比起之前就是在j接口上面多加了一个 @Singleton 注解标识.
运行项目,打印日志如下:

08-22 17:40:30.892 14069-14069/com.zity.dragger2 D/lc: 奥迪
08-22 17:40:30.892 14069-14069/com.zity.dragger2 D/lc: Car对象地址---com.zity.dragger2.Car@30b48f3---Car1对象地址com.zity.dragger2.Car@30b48f3

可以看出他们是使用了同一个对象,也就是 Car 对象在 MainActivity 中只创建了一次。
上面的几个例子让我们简单的了解了 Dagger2 的基本用法,以及单例注解的基本使用,下面我们再编写一个例子,更符合我们实际开发中可以借鉴学习的例子。在 github 上开源的关于 dagger2 的项目大多数都是结合 mvp 开发模式降低耦合的使用。

本例子所使用的 MVP 取数据是在 P 层取,然后直接传给 V 层显示数据,这种 MVP 开发模式有别于在 M 层取数据回调给 P 层,然后 P 层把数据再传给给 V 层做数据显示,在正式开发中我就是使用以下这种 MVP 开发模式。
MainActivity 的代码(对应 MVP 的 V 层):

public class MainActivity extends Activity implements MainContract.IView{
    @Inject
    MainPresenter presenter;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvp);
        // Dagger注入
        DaggerMainComponent.builder().mainModule(new MainModule(this)).build().inject(this);
        presenter.getData();//获取数据
    }
    @Override
    public void showText(String msg) {
        Log.d("lc",msg);
    }

实体类代码(对应 MVP 的 M 层):

public class MainDoman {
    public MainDoman(){
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    private String name;
}

下面这个类我叫它契约类:

public class MainContract {
    interface IView {
        void showText(String msg);
    }
    interface IPresenter {
        void getData();
    }
}

MainModule 代码:

@Module
public class MainModule {
    MainPresenter presenter;
    public MainModule(MainActivity activity) {
        presenter = new MainPresenter(activity);
    }
    @Provides
    @Singleton
    MainPresenter providesMainPresenter() {
        return presenter;
    }
}

MainComponent 的代码:

@Singleton
@Component(modules = MainModule.class)
public interface MainComponent {
    void inject(MainActivity activity);
}

MainPresenter( MVP 的 P 层)代码:

public class MainPresenter implements MainContract.IPresenter {
    MainDoman mainModel;
    private MainContract.IView view;
    public MainPresenter(MainContract.IView view) {
        this.view = view;
        mainModel=new MainDoman();
    }
    @Override
    public void getData() {
        /*
        .......
        这里是写去服务器获取数据的代码
        这一步就省略了,我下面就直接给M的实例赋值了
         */
        mainModel.setName("小强");
        view.showText(mainModel.getName());
    }
}

Log打印日志:

17:00:32.687 12256-12256/com.zity.dragger2 D/lc: 小强

总结:从 Log 成功打印日志可以得出,在 Activity 注入 Presenter 实例成功,也就是 dagger2 依赖注入跟 MVP 开发模式融合在一起了,也达到了解耦的目的,当然我这些例子使用 dagger2 是最基本最简单的例子,实际开发中会更多的结合 @Scope 与@Qualifier 来使用,如果想了解请 移步该博客;看懂他的博客,你会更深一步的了解并且运用 dagger2 。

展开阅读全文

没有更多推荐了,返回首页