Dagger2自己的理解

来源:互联网 发布:sqlserver删除历史数据 编辑:程序博客网 时间:2024/05/18 21:12

这是我觉得看的比较好的Dagger2文章,在这里很感谢这位老兄。

1.导语

Dagger2是一个比较著名的依赖反转注入框架,可以运用在Android以及Java上面使用,由于本人学习Android,看了大量的Dagger2文章刚开始都觉得似懂非懂,在这里作为记录。

2.为什么要使用Dagger2?

  • 使用依赖反转的框架可以降低耦合,这里的耦合也是指的是构造函数和创建对象的耦合,类似建造者模式,如果类的构造器发生了改变,那么所有创建对象的地方都需要作出修改,那么就耦合性太强了。

3.Dagger2的简单使用

Dagger2最重要的是有几个注解,@Inject,@Model,@Commpent这三个注解都是很有用的,如果想要使用依赖注入框架,那么至少要使用到@Inject,@commpent这两个注解,@model主要是用来提供给那种第三方的对象使用的,因为你无法使用@inject注解到第三方的构造器上面,所以@model是用来提供依赖类所需参数的。

public class Person{    public Person(){    }    public void sayHello(){        Log.v("Dagger2","hello,I am a person");    }}public class MainActivity extents AppComponentActivity{@InjectPerson person@Overridepublic void onCreate(Bundle icicle) {    super.onCreate(icicle);}}

* 如果按照上面的写法的话那么是对象是无法赋值的,还需要使用@Commpont这个注解。这个注解可以标注的是接口或者是抽象类。

   @Commpont   public interface AppComponent{       void inject(Activity activity);   }  这里的方法是需要传递一个对象过来,需要通过这个对象去查找里面带  有@inject注解的对象,然后去赋值的。当然如果你不在目标类当中使  用@inject注解的话可以不添加这个方法。例如使用@Model提供一些单  例的工具类的时候可以直接通过Commpont对象来调用里面提供对象的方法即可。

* 接下来在Activity当中如何使用

   public class MainActivity extends AppComActivity{        @Iject        Person person       @override       public void onCreate(Bundle bundle){           DaggerAppComponent.Builder.build().inject(this);           //此时person对象已经赋值了          person.sayHello();       }   }   原理是什么?   这个对象需要去inject,然后这个对象去查看有注解的对象,然后赋值   DaggerAppComponpent是apt自动化生成的,是一个构建者模式。   其实在赋值的时候是有一个顺序的,使用@model注解生成的依赖对象级别是大于构造器添加注解的。   顺序  1.先去@model对象里面去查是否提供有这个对象。                --这个对象是否需要参数?如果需要去@model里面查                --如果没有的话那么去看是否有注解注解这个构造函数

4.高级用法

  • 在使用一些第三方的库时你无法给第三方的类构造器添加#Inject注解,这时间就要使用的是
    @model,@provide注解,用来提供对象的。

    @Modelpublic class AppModel{    private String baseUrl;    private Context context;    //可以传入一些创建其他对象的参数,比如全局的上下文,BaseUrl。。。    public AppModel(String baseUrl,Application context){        this.baseUrl=baseUrl;        this.context=context;    }     @Singleton    @Provides    public Application provideApplication() {     return application;     }    @Singleton    @Provides    public String provideBaseUrl() {      return baseUrl;    }    @Singleton    @Provides    public OkHttpClient provideOkHttpClient() {    return new OkHttpClient.Builder()        .connectTimeout(timeout, TimeUnit.MILLISECONDS)        .writeTimeout(timeout, TimeUnit.MILLISECONDS)        .readTimeout(timeout, TimeUnit.MILLISECONDS).build();   }    @Singleton    @Provides    public Retrofit provideRetrofit() {     return new Retrofit.Builder()        .client(provideOkHttpClient())        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())        .baseUrl(provideBaseUrl())        .addConverterFactory(GsonConverterFactory.create())        .build();  }  @Singleton  @Provides  public ApiService provideApiService() {   return provideRetrofit().create(ApiService.class);  } } //这里只是简单的创建对象,并没有设置OkHttp的相关配置,可以加上全局的请求 头,全局的请求参数,添加对网络的拦截器,日志的拦截器,对Https的支持, 以及缓存的处理。想要用好OKHttp这些都是需要学习的。自己也在摸索当中。
  • 接下来便可以通过注入的方法来使用这些提供的对象了,但是我们一般会把这些作为一个全局的单利的,会在我们的Application当中去初始化,那么你就无法在对象当中通@Inject来初始化了,此时我们可以采取另外的一种方法来做,就是可以在我们的Commponpent类当中定义一些抽象的方法,这些抽象方法的实现就是在@DaggerxxxComponpent当中,它会去根据model生成的provider类当中去赋值的,接下来在代码当中就可以直接通过commponpent.()去调用了,方法名可以自己命名,是根据返回值的对象去对应的对象当中去赋值的。

5.其他的一些注解用法@Qualifier(@named)@Scope(作用域@Singleton)

@Qualifer
  • Component是一个注入器(Injector),同时也起着桥梁的作用,一端是创建类实例端(创建类实例即负责生产类实例,下面会用该词来指代),另一端是目标类端(目标类需要进行依赖初始化的类,下面都会用目标类一词来指代)创建类实例有2个维度可以创建:
    通过用Inject注解标注的构造函数来创建(以下简称Inject维度)
    通过工厂模式的Module来创建(以下简称Module维度)
  • 这2个维度是有优先级之分的,Component会首先从Module维度中查找类实例,若找到就用Module维度创建类实例,并停止查找Inject维度。否则才是从Inject维度查找类实例。所以创建类实例级别Module维度要高于Inject维度。
  • 现在有个问题,基于同一个维度条件下,若一个类的实例有多种方法可以创建出来,那注入器(Component)应该选择哪种方法来创建该类的实例呢?
    我把上面遇到的问题起个名字叫依赖注入迷失。
    那么可以给不同的创建类实例的方法用标识进行标注,用标识就可以对不同的创建类实例的方法进行区分(标识就如给不同的创建类实例方法起了一个id值)。同时用要使用的创建类实例方法的标识对目标类相应的实例属性进行标注。那这样我们的问题就解决了,提到的标识就是Qualifier注解,当然这种注解得需要我们自定义。
    Qualifier(限定符)就是解决依赖注入迷失问题的。

  • dagger2在发现依赖注入迷失时在编译代码时会报错。@named是框架自带的一个限定符字符

@Scope
@Singleton并没有单利的作用,而实现单利的原因正是我们使用的是一个全局的AppCompent。这个静态的对象,在整个生命周期都是有效了实现了静态。而@scope更多的是用来协调AppCompent对象和model对象的,需要它们使用相同的注解,不然就会出错。

@scope真正的作用

更好的管理Component之间的组织方式,不管是依赖方式还是包含方式,都有必要用自定义的Scope注解标注这些Component,这些注解最好不要一样了,不一样是为了能更好的体现出Component之间的组织方式。还有编译器检查有依赖关系或包含关系的Component,若发现有Component没有用自定义Scope注解标注,则会报错。

更好的管理Component与Module之间的匹配关系,编译器会检查 Component管理的Modules,若发现标注Component的自定义Scope注解与Modules中的标注创建类实例方法的注解不一样,就会报错。

可读性提高,如用Singleton标注全局类,这样让程序猿立马就能明白这类是全局单例类。

@commponpent的组织关系

组织Component
我们已经把一个app按照上面的规则划分为不同的Component了,全局类实例也创建了单例模>式。问题来了其他的Component想要把全局的类实例注入到目标类中该怎么办呢?这就涉及到类实例共享的问题了,因为Component有管理创建类实例的能力。因此只要能很好的组织Component之间的关系,问题就好办了。具体的组织方式分为以下3种:
依赖方式
一个Component是依赖于一个或多个Component,Component中的dependencies属性就是依赖方式的具体实现

包含方式
一个Component是包含一个或多个Component的,被包含的Component还可以继续包含其他的Component。这种方式特别像Activity与Fragment的关系。SubComponent就是包含方式的具体实现。

继承方式
官网没有提到该方式,具体没有提到的原因我觉得应该是,该方式不是解决类实例共享的问题,而是从更好的管理、维护Component的角度,把一些Component共有的方法抽象到一个父类中,然后子Component继承。

6.其他的东西

这里重点说下dagger2对目标类进行依赖注入的过程,现在假设要初始化目标类中的其中一个依赖类的实例,那具体步骤就在下面:这里重点说下dagger2对目标类进行依赖注入的过程,现在假设要初始化目标类中的其中一个依赖类的实例,那具体步骤就在下面:

 步骤1:查找Module中是否存在创建该类的方法。 步骤2:若存在创建类方法,查看该方法是否存在参数 步骤2.1:若存在参数,则按从**步骤1**开始依次初始化每个参数 步骤2.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束 步骤3:若不存在创建类方法,则查找Inject注解的构造函数,       看构造函数是否存在参数 步骤3.1:若存在参数,则从**步骤1**开始依次初始化每个参数 步骤3.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束.
原创粉丝点击