Dagger2使用笔记

来源:互联网 发布:网络的好处 英语作文 编辑:程序博客网 时间:2024/06/05 09:04

Dagger2 介绍:

Dagger 是一个依赖注入库,至于什么是依赖注入可以看这篇文章里面也讲了依赖注入的好处。总的来说就是类与类之间解耦。

Dagger2如何使用?

1.添加依赖

首先,在整个项目的 build.gradle文件中的dependencies 节点里添加如下:

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

这是因为我们要用apt 来解析Dagger2库中的各种注解,而不是通过反射来解析。因为java中反射是耗性能的。

然后在主module 中的build.gragle 文件顶部添加

apply plugin: 'android-apt'

然后在dependencies里添加

    //使用APT生成工具,生成需要的DI代码    apt 'com.google.dagger:dagger-compiler:2.5'    //JSR250的jar包,使用这个和使用glassFish的那个一样,仅为了使用@Inject 和@Named注解    provided 'javax.annotation:jsr250-api:1.0'    //Dagger2 的依赖    compile 'com.google.dagger:dagger:2.5'`
添加完这些之后就能开始撸代码了。

2.Dagger2 中的各种注解的使用

先说明两个概念:被注入依赖的类我们称为宿主,注入的类我们成为依赖

1)最简单用法组合:@Inject,@Component

1.在依赖类的构造方法添加注解@Inject :

public class TestData1 {    private  String testString="this is test data String ";    public String getTestString() {        return testString;    }    @Inject    public TestData1() {    }}

2.然后在宿主中要注入的变量添加@Inject注解(例如MainActivity 里面有一个testData1 变量)
这个testData1 变量必须是public或者包内可访问(即变量不添加任何public private的修饰),不然会造成注入失败。这和注入原理有关系后面会说到。

   public class MainActivity extends BaseActivity implements IMainView {    @Inject    TestData1 testData1;    }

3.以上两步都有都有@Inject 注解,这时就会想怎样把他们联系起来呢?好的,@Componet出场了。这是一个注入器,能把testData1 注入到宿主。

@Componentpublic interface MainComponent {    void inject(MainActivity mainActivity);}

这里说明下:

  1. 首先MainComponent 是一个接口,且有@Component注解。
  2. 接口里面有void inject(MainActivity mainActivity)方法。这个方法的关键是要传一个宿主的实例进来。

4.准备工作都做好了,剩下最后一步调用注入方法。在这之前,我们得先做一步build操作,如果IDE是AS的话ctrl+F9 。运行后我们在如下目录:
MainComponet 接口实现类
找到MainComponent接口实现类DaggerMainComponent,这个类是apt 产生的,所以一开始我们的依赖得添加apt。有了这个类我们就可以在宿主MainActivity调用注入。

 @Override     protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ButterKnife.bind(this);        DaggerMainComponent.builder().build().inject(this);//这边注入        Log.d("LOG","test data is "+testData1.getTestString());    }

在运行项目我们我一看到我们的Log信息:
这里写图片描述
看到这个Log说明testData1是不为空,说明注入成功了。这就是一个简单的注入的例子。这不是很Easy吗?那怎么Dagger2 的项目搞这么多注解。那么如果我们有一个要注入的依赖不是自定义的类而是来自第三方库呢?这种要怎么注入?这时@Provide注解就出场了。

2)用法2:@Module,@Provides,@Component的组合。

这里出现了两个新的注解@Module,@Provides这两个东西怎么用呢看如下示例代码:

public class TestData1 {    private  String testString="this is test data String ";    public String getTestString() {        return testString;    }    public TestData1() {    }}

这是依赖类,注意这时它的构造函数已经没有@Inject注解了。但是我们多了一个MainModule类,这种是Module类:

@Modulepublic class MainModule {    @Provides    public TestData1 getTestData(){        return new TestData1();    }

这里要说明下:

  1. Module类被必须注解@Module修饰,此类的里面主要是@Provides修饰的方法。
  2. @Provides 注解只能修饰方法 ,就如中文意思”提供“一样,它修饰的方法就是提供一个依赖的实例。

那么这个凭空多出来的MainModule有什么用呢,我们来看下原来MainComponent

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

和之前的变化就是@Component注解中设置了了一个module这个参数的值。因为MainComponet这个类是要进行注入的,注入要有依赖和宿主。宿主很好理解就是inject方法里面的参数mainAcitivty,至于依赖那就是来自于MainModule的那些@Provides修饰的方法了。所以这边得有MainModule。而且@Component中的Module类可以有多个写法是这样的:

@Component(modules={AXXModule.class,BXXModule.class})

重新通过ctrl+F9来 build项目,重新生成DaggerMainComponent类,我们的宿主代码也得改变:

   @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ButterKnife.bind(this);        DaggerMainComponent.builder().mainModule(new MainModule()).build().inject(this);//这里注入        Log.d("LOG", "test data is " + testData1.getTestString());    }

这里调用build()方法之前先调用了mainModule传一个MainModule实例 给DaggerMainComponent让其能获得依赖的实例。

3)@Singleton使用。

面向对象里面有单例模式,Dagger 2也可以形成单例模式 。@Singleton注解使用首先在对应的Component 要添加@Singletion

@Singleton@Component(modules = AppModule.class)public interface AppComponent {    MainComponent addSub(MainModule module);    void inject(TestActivity testActivity);}

除此之外,要么在预实现单例的类中添加@Singleton

@Singletonpublic class TestData1 {    private  String testString="this is test data String ";    public String getTestString() {        return testString;    }    @Inject    public TestData1() {    }}

要么在对应的@Provide修饰的方法中添加@Singleton

@Modulepublic class AppModule {    @Singleton    @Provides    AppData providesData() {        return new AppData();    }}

以上两中情况选其中一个。
然后我们在宿主中完成注入:

public class TestActivity extends BaseActivity {    @Inject    AppData data, data2;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        DaggerAppComponent.builder().build().inject(this);        Log.d("LOG", "test data --- data2 is " + data + "---" + data2);    }}

以下是运行结果
这里写图片描述
可以看到data和data2 是同一个实例。
对于这个@Singleton 需要注意: 要AppData是单例的,必须保证DaggerAppComponent只被初始化一次 为了注入单例我们可以把DaggerAppComponent在自定义的Application里初始化,然后我们只要在自定义的Application里面获取DaggerAppComponent进行注入。

4)@Subcomponent使用。

现在有这么一种情况ActivityA和ActivityB分别有对应的ComponentA,和ComponentB,但是里面注入的类有一部分是相同的我们不可能同时在两个Component 进行注入,这样太不面向对象了,这时候我们就可以用@Subcomponent。如下:

@Subcomponent(modules = MainModule.class)public interface MainComponent {    void inject(MainActivity mainActivity);}

比如我们共同要注入的类是AppData

@Modulepublic class AppModule {    @Singleton    @Provides    AppData providesData() {        return new AppData();    }}

同时我们共用的Component 也要作改变

@Singleton@Component(modules = AppModule.class)public interface AppComponent {    MainComponent addSub(MainModule module);}

这里多了一个addSub方法。返回的是我们的SubComponent,参数是SubComponent 关联的Module。
在注入页面我们是这样调用的

DaggerAppComponent.builder().appModule(new AppModule()).build().addSub(new MainModule()).inject(this);

5)@Named不同初始化实例的注入。

有这么一种情况:一个类有两种构造方法。一种是带参数的,一种是不带参数的。同时要注入两种实例怎么办?这时候@Named 就有用的,看如下代码:

    @Provides    @Named(MainPresenterImp.DEFAULT)    public RecDataGenerator getDefault() {//通过注解Named 来获取不同的实例        return new RecDataGenerator();    }    @Provides    @Named(MainPresenterImp.NOTDEFAULT)    public RecDataGenerator getNotDefault() {//通过注解Named 来获取不同的实例        return new RecDataGenerator("this is not defualt ");    }

我们在Module做两个生成不同实例的方法,且用@Named 注解来标记,此注解有一个String参数。要和注入的地方对应起来

    public static final String DEFAULT = "default";    public static final String NOTDEFAULT = "notdefault";    @Inject    @Named(NOTDEFAULT)    RecDataGenerator notDefaultgenerator;    @Inject    @Named(DEFAULT)    RecDataGenerator defaultgenerator;

3.总结

Dagger2的各种注解的用法都说到了。至于什么时候用Dagger?我的答案是什么项目都可以用,而且这里的注解没有用反射来解析不用考虑性能的问题。Dagger2的项目会在build 文件夹下面生成很多新的类,多看这些类对了解这个框架有好处。

相关代码:https://github.com/wulongfang/android-mvp-dagger.git
原创粉丝点击