Dagger2

作者: 面向星辰大海的程序员 | 来源:发表于2020-04-28 23:51 被阅读0次

    Dagger2:实现依赖注入,解耦

    Dagger2起源于Dagger,是一款基于Java注解来实现的完全在编译阶段完成依赖注入的开源库,主要用于模块间解耦、提高代码的健壮性和可维护性。Dagger2在编译阶段通过apt利用Java注解自动生成Java代码,然后结合手写的代码来自动帮我们完成依赖注入的工作。

    起初Square公司受到Guice的启发而开发了Dagger,但是Dagger这种半静态半运行时的框架还是有些性能问题(虽说依赖注入是完全静态的,但是其有向无环图(Directed Acyclic Graph)还是基于反射来生成的,这无论在大型的服务端应用还是在Android应用上都不是最优方案)。因此Google工程师Fork了Dagger项目,对它进行了改造。于是变演变出了今天我们要讨论的Dagger2,所以说Dagger2其实就是高配版的Dagger。

    Dagger2注解

    Dagger2是基于Java注解来实现依赖注入的,那么在正式使用之前我们需要先了解下Dagger2中的注解。Dagger2使用过程中我们通常接触到的注解主要包括:@Inject, @Module, @Provides, @Component, @Qulifier, @Scope, @Singleten。

    @Inject:@Inject有两个作用,一是用来标记需要依赖的变量,以此告诉Dagger2为它提供依赖;二是用来标记构造函数,Dagger2通过@Inject注解可以在需要这个类实例的时候来找到这个构造函数并把相关实例构造出来,以此来为被@Inject标记了的变量提供依赖;

    @Module:@Module用于标注提供依赖的类。你可能会有点困惑,上面不是提到用@Inject标记构造函数就可以提供依赖了么,为什么还需要@Module?很多时候我们需要提供依赖的构造函数是第三方库的,我们没法给它加上@Inject注解,又比如说提供依赖的构造函数是带参数的,如果我们使用简单的使用@Inject标记它,那么他的参数又怎么来呢?@Module正是帮我们解决这些问题的。

    @Provides:@Provides用于标注Module所标注的类中的方法,该方法在需要提供依赖时被调用,从而把预先提供好的对象当做依赖给标注了@Inject的变量赋值;

    @Component:@Component用于标注接口,是依赖需求方和依赖提供方之间的桥梁。被Component标注的接口在编译时会生成该接口的实现类(如果@Component标注的接口为CarComponent,则编译期生成的实现类为DaggerCarComponent),我们通过调用这个实现类的方法完成注入;

    @Qulifier:@Qulifier用于自定义注解,也就是说@Qulifier就如同Java提供的几种基本元注解一样用来标记注解类。我们在使用@Module来标注提供依赖的方法时,方法名我们是可以随便定义的(虽然我们定义方法名一般以provide开头,但这并不是强制的,只是为了增加可读性而已)。那么Dagger2怎么知道这个方法是为谁提供依赖呢?答案就是返回值的类型,Dagger2根据返回值的类型来决定为哪个被@Inject标记了的变量赋值。但是问题来了,一旦有多个一样的返回类型Dagger2就懵逼了。@Qulifier的存在正式为了解决这个问题,我们使用@Qulifier来定义自己的注解,然后通过自定义的注解去标注提供依赖的方法和依赖需求方(也就是被@Inject标注的变量),这样Dagger2就知道为谁提供依赖了。----一个更为精简的定义:当类型不足以鉴别一个依赖的时候,我们就可以使用这个注解标示;

    @Scope:@Scope同样用于自定义注解,我能可以通过@Scope自定义的注解来限定注解作用域,实现局部的单例;

    @Singleton:@Singleton其实就是一个通过@Scope定义的注解,我们一般通过它来实现全局单例。但实际上它并不能提前全局单例,是否能提供全局单例还要取决于对应的Component是否为一个全局对象。

    注解使用:
    在项目模块build.gradle文件中添加
    dependencies {
    ...
    //自己版本号改为最新的
    implementation 'com.google.dagger:dagger-android:2.27'
    implementation 'com.google.dagger:dagger-android-support:2.27'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.27'
    annotationProcessor 'com.google.dagger:dagger-android-processor:2.27'
    }

    官方链接
    https://github.com/google/dagger

    @Inject

    //提供实例对象的类
    public class Apple implements Foot {
        private final String TAG = "Foot";
        private String type = "普通";
    //在构造方法加 @Inject  标明这是提供实例对象的构造方法,只能添加在其中某一个构造方法
        @Inject
        public Apple() {
            //@Inject只能注释其中一个构造方法
        }
        public void appleType(String from) {
            type = from;
        }
        @Override
        public void eatFoot() {
            MyLog.INSTANCE.i(TAG, "吃" + type + "苹果");
        }
    }
    
    //注射器接口
    import dagger.Component;
    @Component
    public interface EatComponent {
        void inject(Eat eat);
    }
    
    import javax.inject.Inject;
    public class Eat {
        public Foot getFoot() {
            return apple;
        }
    //在成员变量加 @Inject  标明这个成员变量需要注解提供一个实例对象
        @Inject
        public Apple apple;
        public Eat() {
    //提供实例对象的类和注射器接口都写好后构建项目就会生成Dagger开头对应的注射器类
    //将需要注射实例对象的实例传入
            DaggerEatComponent.builder().build().inject(this);
           MyLog.INSTANCE.i(TAG,apple.eatFoot());//输出吃普通水果
        }
    }
    

    如果不能在提供实例类的内部添加@Inject 比如第三方框架等
    第二种提供实例的方法:
    @Module 注释提供实例的类
    @Provides 注释 @Module注释过的类里的方法,需要准备好提供的实例,可以注释多个方法

    import dagger.Module;
    import dagger.Provides;
    @Module
    public class AppleDagger2Module {
        public AppleDagger2Module() {
        }
        @Provides
        public Apple provideApple() {
            Apple apple = new Apple();
            apple.appleType("进口");
            return apple;
        }
    }
    
    //注射器接口也得改一下
    import dagger.Component;
    //标明提供实例的modules=上述的AppleDagger2Module
    @Component(modules = AppleDagger2Module.class)
    public interface EatModuleComponent {
        void inject(Eat eat);
    }
    import javax.inject.Inject;
    public class Eat {
        public Foot getFoot() {
            return apple;
        }
    //在成员变量加 @Inject  标明这个成员变量需要注解提供一个实例对象,不管使用哪种方法提供实例对象,被注射的都用@Inject
        @Inject
        public Apple apple;
        public Eat() {
             DaggerEatModuleComponent.builder().build().inject(this);//使用@Module+@Provides生成的类注射实例对象
           MyLog.INSTANCE.i(TAG,apple.eatFoot());//输出吃进口水果
        }
    }
    

    我们提到@Inject和@Module都可以提供依赖,那如果我们即在构造函数上通过标记@Inject提供依赖,又通过@Module提供依赖Dagger2会如何选择呢?具体规则如下:

    • 步骤1:首先查找@Module标注的类中是否存在提供依赖的方法。

    • 步骤2:若存在提供依赖的方法,查看该方法是否存在参数。

      • a:若存在参数,则按从步骤1开始依次初始化每个参数;
      • b:若不存在,则直接初始化该实例,完成一次依赖注入。
    • 步骤3:若不存在提供依赖的方法,则查找@Inject标注的构造函数,看构造函数是否存在参数。

      • a:若存在参数,则从步骤1开始依次初始化每一个参数
      • b:若不存在,则直接初始化该实例,完成一次依赖注入。
      @Inject
        public FruitsInfo appleInfo;//成员变量又得写一波注射器接口,下面的方法参数就省略了写注释器接口
    
        @From
        @Provides
        public Apple provideFromApple(FruitsInfo fruitsInfo) {//如果有参数参数里必须有提供实例的注释,如上述,这是一个迭代,不然直接报错
            Apple apple = new Apple();
            fruitsInfo.setFrom("进口");
            appleInfo.setFrom("自家种的");
            apple.appleType(appleInfo.getFrom());
            return apple;
        }
    

    @Scope作用域

    public class Apple implements Foot {
        private final String TAG = "Foot";
        private String type = "普通";
        //    @Inject
        public Apple() {
            //@Inject只能注释其中一个构造方法
            MyLog.INSTANCE.i(TAG, "Apple");
        }
        public void appleType(String from) {
            type = from;
        }
        @Override
        public void eatFoot() {
            MyLog.INSTANCE.i(TAG, "吃" + type + "苹果");
        }
        @Scope
        public @interface AppleSigle {
    //@Scope不可以直接用,自定义作用域
        }
    }
    
       @Apple.AppleSigle//
        @Local
        @Provides
        public Apple provideLocalApple() {
            Apple apple = new Apple();
            apple.appleType("本地");
            return apple;
        }
    
    @Apple.AppleSigle
    @Component(modules = AppleDagger2Module.class)
    public interface EatModuleComponent {
        void inject(Eat eat);
    }
    
      @AppleDagger2Module.Local
        @Inject
        public Apple locaAapple;
    
        @AppleDagger2Module.Local
        @Inject
        public Apple fromapple;
    
        public Eat() {
            DaggerEatModuleComponent.builder().build().inject(this);
        }
    
        public void eatFoot() {
            locaAapple.eatFoot();
            fromapple.eatFoot();
    //打印出两次吃苹果,但是构造方法只打印一次
        }
    

    Dagger2与MVP

    对于一个应用而言我们需要对它抽象出各个层面,而在MVP架构中它将UI界面和数据进行隔离,所以我们的应用也就分为三个层次。

    • View: 对于View层也是视图层,在View层中只负责对数据的展示,提供友好的界面与用户进行交互。在Android开发中通常将Activity或者Fragment作为View层。

    • Model: 对于Model层也是数据层。它区别于MVC架构中的Model,在这里不仅仅只是数据模型。在MVP架构中Model它负责对数据的存取操作,例如对数据库的读写,网络的数据的请求等。

    • Presenter:对于Presenter层他是连接View层与Model层的桥梁并对业务逻辑进行处理。在MVP架构中Model与View无法直接进行交互。所以在Presenter层它会从Model层获得所需要的数据,进行一些适当的处理后交由View层进行显示。这样通过Presenter将View与Model进行隔离,使得View和Model之间不存在耦合,同时也将业务逻辑从View中抽离。

    下面通过MVP结构图来看一下MVP中各个层次之间的关系。

    mvp.png

    在MVP架构中将这三层分别抽象到各自的接口当中。通过接口将层次之间进行隔离,而Presenter对View和Model的相互依赖也是依赖于各自的接口。这点符合了接口隔离原则,也正是面向接口编程。在Presenter层中包含了一个View接口,并且依赖于Model接口,从而将Model层与View层联系在一起。而对于View层会持有一个Presenter成员变量并且只保留对Presenter接口的调用,具体业务逻辑全部交由Presenter接口实现类中处理。

    如果你的项目是采用MVP架构的,那么结合Dagger2将会是一件非常棒的体验,它让M-V-P进一步解藕,架构更清晰。

    Android 中使用MVP+Dagger2

    //BaseActivity 继承DaggerAppCompatActivity
    public abstract class BaseActivity extends DaggerAppCompatActivity {
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            AndroidInjection.inject(this);//注入activity
        }
    }
    
    //M,提供M实例
    public class RegisterModule implements RegisterContract.Module {
        @Inject
        public RegisterModule() {
        }
    }
    //V接收注入的P
    public class RegisterActivity extends BaseActivity implements RegisterContract.View {
        @Inject
        protected RegisterPresenter mPresenter;//接收注入的Presenter实例
    }
    
    
    //P,提供P实例
    public class RegisterPresenter extends AbsBasePresenter<RegisterModule, RegisterActivity> implements RegisterContract.Presenter {
        @Inject
        public RegisterPresenter(RegisterModule module, RegisterActivity view) {
            mModule = module;
            mView = view;
        }
    }
    
    @Module
    public abstract class RegisterActivityModule {
        @Provides
        static String provideName() {
            return RegisterActivity.class.getName();
        }
    
    }
    @Module
    public abstract class AllActivitysModule {
        @ContributesAndroidInjector(modules = RegisterActivityModule.class)
        abstract RegisterActivity contributeRegisterActivity();
    
    }
    
    //在注射器接口添加各种Module
    @Component(
            modules = {
                    AndroidSupportInjectionModule.class,
                    AllActivitysModule.class//所有的Activity都在这里添加
            }
    )
    public interface AppComponent extends AndroidInjector<App> {
    }
    
    //Application继承DaggerApplication 
    public class App extends DaggerApplication {
        @Override
        public void onCreate() {
            super.onCreate();
        }
        @Override
        protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
    //返回生成的注射器
            return DaggerAppComponent.create();
        }
    }
    

    全程不用new实例,M、V、P三层关系都由注解完成,高度解耦。

    相关文章

      网友评论

          本文标题:Dagger2

          本文链接:https://www.haomeiwen.com/subject/cdqwwhtx.html