google四件套之Dagger2。从入门到爱不释手,之:Dagger2基础知识及在Java中使用(1)

前言

网上都说Dagger2是比较难上手的,我在看了大量资料和使用时也遇到了不少不懂或者模糊的知识点,并且大部分博客资料都比较古老。忽然有那么一瞬间,忽然明白了因此然,故总结了4篇文章。话说在java中使用仍是很繁琐的,不要怕带你真正上手,并运用到咱们的Android项目中去。java

本次Dagger2讲解总共分4篇:
一、Dagger2基础知识及在Java中使用(1)
二、Dagger2基础知识及在Java中使用(2)
三、Dagger2进阶知识及在Android中使用
四、Dagger2华丽使用在MVP框架中android

首先简单申明下,Dagger2的好处不是本文的重点。你们能够自行百度。Dagger2是依赖注解框架,像咱们之间的butterknife也是这样的框架,想这样的框架依赖通常都有2行。第二行是以annotationProcessor开头的。这实际上是apt的工具,并且这样的依赖注解框架不会影响性能(不是反射机制),在编译的时候,apt把要用的代码生成。因此大可放心使用
再举我理解的例子(你们不要全信,哈哈稍微不恰当):@Component至关于一个注射器(记住是接口);@Module至关于注射液,就是数据源(记住这里是类或者抽象类),此时要把注射液放入指定哪一个注射器如:@Component( modules = ... );@Inject 至关于标注被注射体。git

以后的讲解都是走完简单的流程,实现功能,而后讲大概理解。贴在博客上的代码,可能会省略部分代码,便于理解。github上的Demo及注释,很是详细,接近完美0-0#
若是是彻底没了解过,关于Dagger一些标注的具体介绍和理解,推荐这里有3篇介绍标注的意思和怎么工做的github

首先添加依赖

implementation 'com.google.dagger:dagger:2.24'
annotationProcessor "com.google.dagger:dagger-compiler:2.24"
复制代码

一、@Inject & @Component 的简单使用(不带@Module)

首先随便定义个类:Person,无参构造方法用@Inject标注:bash

public class Person {
    @Inject
    public Person() {

    }
}
复制代码

而后定义咱们的 Component(这里稍微提一下,若是一个页面定义多个Component,你build的时候报错,是否是)
这里的命名规则最好是以咱们页面类名+Component,这样比较清晰。用@Component标注,里面有个void方法,方法名随意定,建议用inject最好,固然也是清晰,参数是咱们须要依赖注解的页面:
@Component
public interface AstudyActivityComponent {
    void injectTo(AstudyActivity astudyActivity);
}
复制代码

作好上面步骤后,点开studio里build标签下的Make Project。让apt帮咱们生成代码,通常生成代码为Dagger+你定义Component的类名。

以后这个步骤再也不重复,就是你写完准备代码的时候必定要让apt生成代码,Make Project下

而后在咱们的Activity里:app

public class AstudyActivity extends BaseActivity {
    @Inject
    Person person;
    
    @Override
    //这里是我封装的onCreate,省略部分代码,只为理解,以后都请忽略!
    protected void processLogic() {
        //第一种
        DaggerAstudyActivityComponent.create().injectTo(this);
        //第二种
        //DaggerAstudyActivityComponent.builder().build().injectTo(this);
    }
}
复制代码

在咱们的Activity里build下咱们的Component,而后注册在咱们的Activity里,就可使用经过咱们的@Inject使用咱们的person了。
这里初始化有2种:
一、DaggerAstudyActivityComponent.create().injectTo(this);
二、DaggerAstudyActivityComponent.builder().build().injectTo(this); 这个使用module传值必定要使用框架

那么一个简单的使用就实现了,这里忽略了new的过程,从这个过程就知道他解耦的实现了。ide

简单使用大体步骤( 看懂请略过 ):

  • 第一步:用Inject标注,告诉dagger2能够实例化这个类,如:Person
  • 第二步:使用注解Component,表示要将依赖注入到AstudyActivity里
  • 第三步:使用android studio build下的Make Project生成代码,使他自动生成DaggerComponent生成的类,类名是:Dagge+咱们定义的Component的名字

二、带@Module的使用

为何会有module的概念,好比上面的Person的构造方法能够用@Inject标注,可是引入的第三方库但是没有办法加的,因此这里使用@Module能够解决这个问题。
这里咱们定义个Human,伪装他是第三方类库,里面没有使用@Inject函数

public class Human {
    public Human() {

    }
}
复制代码


接下的步骤先定义咱们的数据源Module,也就是先定义初始化的地方,以前Person的构造方法是用@Inject。首先命名规则最好加上Module,用@Module标注。而后里面定义个方法,用 @Provides标注。返回值为咱们须要初始化的类,方法名最好是以Provides结尾。其实这里能够定义多个方法,后面说工具

@Module
public class BstudyActivityModule {
    @Provides
    Human humanProvides(){
       return new Human();
    }
}
复制代码


而后是咱们的Component。这里与以前不一样的是(modules = BstudyActivityModule.class),这就至关于把注射液放进注射器。这里能够有多个Module,后面说

@Component(modules = BstudyActivityModule.class)
public interface BstudyActivityComponent {
    void injectTo(BstudyActivity bstudyActivity);
}
复制代码

Make Project后,在Activity里操做与以前如出一辙。

带Module使用大体步骤( 看懂请略过 )

  • 一、假设Human不可随意更改,没有@Inject标注(第三方类库,不是你项目里的代码确定没有@Inject)用@module标注BstudyActivityModule,用@Provides标注方法的返回值就是咱们须要inject的类型
  • 二、编写Component接口使用@Component标注这个接口,并使用modules=的方法连接上第一步中编写的Module类;
  • 三、接下来就和AstudyActivity中的使用方式同样了

三、经过Module传参

这个其实不重要,重要的引出4,5的概念。明白这步,后面才好理解。
首先咱们假设2个类,女人类,和灵魂类:且灵魂类有个钱的属性。灵魂类又是女人的属性。灵魂类以下:

public class Soul {
    private int money;
    public Soul() {

    }
    public int getMoney() {
        return money;
    }
    public void setMoney(int money) {
        this.money = money;
    }
}
复制代码

女人以下:

public class Woman {
    private Soul soul;

    public Soul getSoul() {
        return soul;
    }

    public Woman(Soul soul) {
        this.soul = soul;
    }
}
复制代码


首先仍是定义咱们的Module先。既然能够传参,固然是有个money的属性。最终咱们依赖注解是要使用Woman类。咱们的providesWoman方法用@Provides标注,这个时候他回去找Soul的初始化,先经过@Provides去找Soul。这个时候找到了providesSoul。这样就造成了女人类。假如这个时候没有providesSoul。他会去找Soul类里有没有用@Inject标注的构造函数。若是尚未,那么很差意思。出错

@Module
public class CstudyModule {
    private int money;
    
    public CstudyModule(int money) {
        this.money = money;
    }

    @Provides
    Soul providesSoul() {
        Soul soul = new Soul();
        soul.setMoney(this.money);
        return soul;
    }

    @Provides
    Woman providesWoman(Soul soul) {
        return new Woman(soul);
    }
}
复制代码

接下来是Component,没有变化

@Component(modules = CstudyModule.class)
public interface CstudyActivityComponent {
    void injectTo(CstudyActivity cstudyActivity);
}
复制代码

Activity有些许变化,固然是传参了。咱们给女人的灵魂传了100块,对,女人只值100块!

public class CstudyActivity extends BaseActivity {
    @Inject
    Woman woman;
    @Override
    protected void processLogic() {
        DaggerCstudyActivityComponent.builder()
            .cstudyModule(new CstudyModule(100))
            .build().injectTo(this);
    }
}
复制代码

注意点( 看懂请略过 ):

  • 在Module的构造函数带有参数且参数被使用的状况下,所生产的Component类就没有create()方法了。
  • 在这里的module若是没有providesSoul()方法的话,还有一种状况只要在Soul的构造方法有@Inject也是可行的。

四、使用@Component.Builder(需先了解 三、经过Module传参)

咱们把三、经过Module传参apt生成的代码点开DaggerCstudyActivityComponent;看下图是否是发现了一个Builder类,这是apt帮咱们自动生成的,咱们固然也能本身实现

仍是以 三、经过Module传参的例子,咱们不用系统帮咱们生成的Builder,本身定义。前面的步骤都同样,直接来看咱们的Component。
本身定义个接口类Builder,并用@Component.Builder标注里面有2个方法:

  • 方法一:是返回值Builder的方法,这里若是传module就会以咱们传的为主,不然他会帮咱们生成一个money为0的module。固然你也随意传数据类型,只不过无效。能够试试,
  • 方法二:是返回值为当前Component的方法,方法名其实均可以自定义,当最好以规范为主,用习惯了就明白了
@Component(modules = CstudyModule.class)
public interface DstudyActivityComponent {
    void injectTo(DstudyActivity dstudyActivity);
    
    @Component.Builder
    interface Builder {
        Builder cstudyModule(CstudyModule cstudyModule);
        DstudyActivityComponent build();
    }
}
复制代码


Activity里使用是同样的。只不过咱们把系统自动帮咱们生成的,本身去写了而已。仍是贴下Activity代码吧

public class DstudyActivity extends BaseActivity {
    @Inject
    Woman dWoman;
    @Override
    protected void processLogic() {
        DaggerDstudyActivityComponent.builder()
            .cstudyModule(new CstudyModule(100))
            .build().injectTo(this);
    }
}
复制代码

大体理解和总结为( 看懂请略过 ):

  • 经过咱们cstudy的内容,你能够点开cstudyModule查看源码,能够看到有个Builder cstudyModule(CstudyModule cstudyModule){}。这是dagger2自动生成的(你还能够经过,app/build/generated/source/apt/debug/你的包名/DaggerAppComponent.java 目录下找到)


因此@Component.Builder的用法,用module传参的例子。其余都不用变,要变的是Component,定义个Builder并用@Component.Builder标注。这里有2个方法:

  • 方法一:是返回值Builder的方法,这里若是传module就会以咱们传的为主,不然他会帮咱们生成一个money为0的module。固然你也随意传数据类型,只不过无效。能够试试
  • 方法二:是返回值为当前Component的方法,方法名其实均可以自定义,当最好以规范为主,用习惯了就明白了

五、使用@BindsInstance(需先了解 四、使用@Component.Builder)

这个时候你又说了,传参,咱们老是要new CstudyModule(100)。原本说Dagger2在使用的时候省略new的过程,解耦。但这里还要new,很low是否是。不急不急,强大的google把一切都想好了。这个时候遇到一个新的标注@BindsInstance。
@BindsInstance 大体这里能够理解为帮咱们省去写类的构造方法,而直接去赋值

省掉构造方法,那么固然是首先改咱们的Module。咱们去掉Module的构造方法及money成员变量属性,把money加到providesSoul里成型参。看到这里,这里又可理解为@BindsInstance 其实去找@Provides标记的方法的参数,假如类型一致就去初始化

@Module
public class EstudyModule {
    @Provides
    Soul providesSoul(int money) {
        Soul soul = new Soul();
        soul.setMoney(money);
        return soul;
    }

    @Provides
    Woman providesWoman(Soul soul) {
        return new Woman(soul);
    }
}
复制代码


而后是修改的Component,改完Module,固然是modules = EstudyModule.class。这些我就忽略了,看了上面的步骤你也明白,我就直接说关键地方了。用@BindsInstance标注咱们返回值为Builder的方法。里面的参数改为咱们的int Money。固然改为咱们用@Provides标注的类型其实均可以,只不过这里你若是改为Soul soul,固然你初始化仍是要传new Soul。过程就是这个过程

@Component(modules = EstudyModule.class)
public interface EstudyActivityComponent {
    void injectTo(EstudyActivity estudyActivity);
    
    @Component.Builder
    interface Builder {
        @BindsInstance
        Builder initMoney(int money);
        EstudyActivityComponent build();
    }
}
复制代码


最后是咱们的Activity

public class EstudyActivity extends BaseActivity {
    @Inject
    Woman woman;
    @Override
    protected void processLogic() {
        DaggerEstudyActivityComponent.builder()
            .initMoney(100)
            .build().injectTo(this);
    }
}
复制代码

看到这里,是否是以为Dagger2还比较有意思。更有意思的在后面。固然也愈来愈绕了,可是你得兴奋起来,精髓啊。

六、Component依赖Component,使用dependence

这里咱们以Activity和Fragment为例。假设咱们再Activity依赖注入Human类,此时在Fragment里使用
先看定义Module,和以前同样,没什么区别

@Module
public class FstudyActivityModule {
    @Provides
    Human providesHuman() {
        return new Human();
    }
}
复制代码


再建ActivityComponent固然这里,你也能够加上void inject(FstudyActivity fstudyActivity)。重要一点是咱们要把依赖注入的类返回出去,定义方法provideHuman,由于是Component依赖Component。因此也能理解

@Component(modules = FstudyActivityModule.class)
public interface FstudyActivityComponent {
    Human provideHuman();
}
复制代码


再使用dependencies建FragmentComponent暂且能够理解为子Component,由于后面真的有子Component。dependencies = FstudyActivityComponent.class写上咱们的父Component。后面是注入到Fragment里

@Component(dependencies = FstudyActivityComponent.class)
public interface TestFragmentComponent {
    void inject(TestFragment testFragment);
}
复制代码


在Activity里,要先生成ActivityComponent,而后提供个方法,把父Component提供给Fragment

public class FstudyActivity extends BaseActivity {
    private FstudyActivityComponent fstudyActivityComponent;
    @Override
    protected void processLogic() {
        fstudyActivityComponent = DaggerFstudyActivityComponent.create();
    }

    public FstudyActivityComponent getFstudyActivityComponent() {
        return fstudyActivityComponent;
    }
}
复制代码


在Fragment里

public class TestFragment extends BaseFragment {
    @Inject
    Human human;
    @Override
    protected void processLogic(Bundle savedInstanceState) {
        FstudyActivityComponent fstudyActivityComponent = ((FstudyActivity) getActivity()).getFstudyActivityComponent();
        DaggerTestFragmentComponent.builder()
                .fstudyActivityComponent(fstudyActivityComponent)
                .build().inject(this);
    }

}
复制代码

好了,在fragment可使用human了。在java里使用,确实很绕,代码多的让你难以接受。建议先理解,后面出的一篇在Android中使用,你会很爽。

大体理解和总结为( 看懂请略过 ):

  • 一、假设咱们用Human注入,这里的FstudyActivityModule 和以前的Module同样,正常
  • 二、FstudyActivityComponent把要注入的类返回
  • 三、在TestFragment方面,咱们新建一个TestFragmentComponent 依赖 FstudyActivityComponent;
  • 四、在FstudyActivity自定义一个方法把FstudyActivityComponent提供出去,供TestFragment使用
  • 五、在TestFragment,注册下就OK了。很绕,我的建议先明白这个流程就行了

七、Component依赖Component,使用@subComponent(这个和 【标题6】 实现的是同一个效果)

虽然是实现同一个效果,可是方式不一样,目的是让你更多了解Dagger2。一样以上面的例子。Module和上面同样不变(我这里是为了Demo区域化,虽然类名不一样,可是内容是一致的)
首先建子Component,FragmenComponent,用@Subcomponent标注,并注入咱们的Fragment里。为何先建子Component呢。由于子Component要在父Component返回,绕不绕!!

@Subcomponent
public interface DemoFragmentComponent {
    void inject(DemoFragment demoFragment);
}
复制代码


而后是父Component,ActivityComponent,父Component一切正常,返回值是子Component

@Component(modules = GstudyActivityModule.class)
public interface GstudyActivityComponent {
    DemoFragmentComponent demoFragmentComponent();
}
复制代码


在Activity里的操做同样,初始化咱们的父Component,并提供方法,返回父Component,供Fragment使用。
而后是Fragment里

public class DemoFragment extends BaseFragment {
    @Inject
    Human human;
    @Override
    protected void processLogic(Bundle savedInstanceState) {
        GstudyActivityComponent gstudyActivityComponent = ((GstudyActivity) getActivity()).getGstudyActivityComponent();
        gstudyActivityComponent
            .demoFragmentComponent()
            .inject(this);
    }

}
复制代码

这样就成功了,能够在Fragment使用human了。看明白了标题6,其实标题7原理是同样的。

大体理解和总结为( 看懂请略过 ):

  • 一、先建一个子类Component,用@subComponent标注,DemoFragmentComponent
  • 二、而后建父类Component: GstudyActivityComponent,定义个方法,返回子类Component。
  • 三、在GstudyActivity自定义一个方法把GstudyActivityComponent提供出去,供DemoFragment使用
  • 四、在DemoFragment,注册下,就行了。大体和dependencies相似
  • 注意:但注册的时候写法不一样,以前是经过子Component传入父Component;而这里是从父Component中获取子Component,而后直接inject

八、Component依赖Component,使用 @Subcomponent.Builder(和【标题6】&【标题7】实现的是同样的效果)

效果同样,方式不一样,目的仍是更了解Dagger2。能够看到这里的标注是@Subcomponent.Builder。因此和使用@Subcomponent相似。


好了,仍是以上面的例子为例。这里须要改的是父Component和子Componet。这个时候咱们难免想到@Component.Builder的用法。是否是同样呢。这个时候我只能说相似,可是又不同。毕竟这里多了个sub。咱们先看下@Component.Builder的用法,拷贝以前代码(不知道再去回顾下【标题4】)

@Component(modules = CstudyModule.class。)
public interface DstudyActivityComponent {
    void injectTo(DstudyActivity dstudyActivity);

    @Component.Builder
    interface Builder {
        Builder cstudyModule(CstudyModule cstudyModule);
        DstudyActivityComponent build();
    }
}
复制代码


咱们按照找个方式去写@Subcomponent.Builder。,@Subcomponent.Builder要使用确定是在@Subcomponent下,毋庸置疑。首先发现没有modules = CstudyModule.class。被@Subcomponent取代了。没有Module咱们就使用无参

@Subcomponent
public interface OtherFragmentComponent {
    void inject(OtherFragment otherFragment);
    @Subcomponent.Builder
    interface Builder {
        Builder noModule();    
        OtherFragmentComponent build();
    }
}
复制代码

我先告诉告诉你运行结果把,运行结果报错了。

@Subcomponent.Builder types must have exactly one zero-arg method, and that method must return the @Subcomponent type. Already found: hstudyActivityModule()

报错信息以下:意思是不须要Build返回值方法,经过Already found: hstudyActivityModule()知道,已经发现了咱们的Module。咱们再想一想这个标注的名称sub,不就是子Component继承父Componet吗。并且Dagger2内部已经默认了,因此这里没有Builder返回值方法。因此正确的子Component

@Subcomponent
public interface OtherFragmentComponent {
    void inject(OtherFragment otherFragment);
    @Subcomponent.Builder
    interface Builder {
        OtherFragmentComponent build();
    }
}
复制代码


接下来是父Component,返回值固然是咱们的Builder。

@Component(modules = HstudyActivityModule.class)
public interface HstudyActivityComponent {
    OtherFragmentComponent.Builder sonbuilder();
}
复制代码

有人就疑惑了不能够返回子Component吗。咱们假如此时返回子Component,我先告诉你运行报错,信息以下:

Components may not have factory methods for subcomponents that define a builder.

大概意思是:用了@Subcomponent.Builder的话,Component没有工厂模式方法去建立咱们的子Component。好了,就这样,请原谅个人英语四级!!


Activity仍是和以前同样,初始化咱们的父Component,并经过方法返回。Fragment里使用依赖以下

public class OtherFragment extends BaseFragment {
    @Inject
    Human human;
    @Override
    protected void processLogic(Bundle savedInstanceState) {
        HstudyActivityComponent hstudyActivityComponent = ((HstudyActivity) getActivity()).getHstudyActivityComponent();
        hstudyActivityComponent.
                sonbuilder().build().inject(this);
    }

}
复制代码

好了,绕来绕去,功能实现了!看到这里对Dagger2大体了解了吧。

因为在java里使用比较多。临时决定java分2篇。否则博客太长,也没人看。

本文github Demo地址

花了老大劲,别误会,不是要钱。能不能留下个足迹star啊

相关文章
相关标签/搜索