IOC架构设计之Dagger2架构设计(三)
阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680
一、概述
Dagger2依赖注入框架的好处:
- 依赖的注入和配置独立于组件之外
- 依赖对象是在一个独立、不耦合的地方初始化,当初始化方式改变的时候修改的代码少。
- 依赖注入使得单元测试更加简单。
Dagger2相对于其它框架的优点:
- 编译期生成代码,有错误会在编译期报出。
- 错误可追踪。
- 易于调试。
Dagger2的缺点:
- 缺少灵活性。
- 没有动态机制。
二、Dagger2的注解
Dagger2的注解主要有以下七类:
@Inject:这个注解有两个作用:在目标类中标记成员变量告诉Dagger这个类型的变量需要一个实例对象;标记依赖类中的构造方法,告诉Dagger我可以提供这种类型的依赖实例。@Component:用来标记接口或者抽象类,也被称为注入器,是@Inject和@Module的桥梁,所有的Component都可以通过它的modules知道它所提供的依赖范围,一个Componet可以依赖一个或多个Component,并拿到被依赖Component暴露出来的实例,Componenet的dependencies属性就是确定依赖关系的实现。@Module:用来标记类,一般类名以Module结尾,Module的主要作用是用来集中管理@Provides标记的方法,我们定义一个被@Module注解的类,Dagger就会知道在哪里找到依赖来满足创建类的实例,Module的一个重要特征是被设计成区块并可以组合在一起。@Provides:对方法进行注解,并且这些方法都是有返回类型的,告诉Dagger我们向如何创建并提供该类型的依赖实例(一般会在方法中new出实例),用@Provides标记的方法,推荐用provide作为前缀。@Qualifier:限定符,当一个类的类型不足以标示一个依赖的时候,我们就可以用这个注解,它会调用DataModule中方法来返回合适的依赖类实例。@Scope:通过自定义注解来限定作用域,所有的对象都不再需要知道怎么管理它的实例,Dagger2中有一个默认的作用域注解@Singleton,通常用来标记在App整个生命周期内存活的实例,也可以定义一个@PerActivity注解,用来表明生命周期要与Activity一致。@SubComponent:如果我们需要父组件全部的提供对象,我们就可以用包含方式,而不是用依赖方式,包含方式不需要父组件显示显露对象,就可以拿到父组件全部对象,且SubComponent只需要在父Component接扣中声明就可以了。
三、Dagger2的简单应用 - @Inject和@Component
第一步:基础配置,在build.gradle中添加相应的依赖:
第二步:User作为目标类中需要实例化的成员对象,给其构造函数添加@Inject标签:
第三步:声明Component:
第四步:在目标类中添加注解@Inject,并根据我们第3步中声明的Component,调用DaggerXXX方法来进行注入:
上面这个例子有两个缺点:
- 只能标记一个构造方法,因为如果标记两个以上,不知道要用哪一个构造提供实例。
- 不能标记其它我们不能修改的类,例如第三方库。
- 如果用
@Inject标记的构造函数如果有参数,那么这个参数也需要其它地方提供依赖,而类似于String这些我们不能修改的类,只能用@Module中的@Provides来提供实例了。
四、采用@Module来提供依赖
采用@Module标记的类提供依赖是常规套路,@Module标记的类起管理作用,真正提供依赖实例靠的是@Provides标记的带返回类型的方法。
第一步:和上面类似,我们定义一个依赖类,但是它的构造方法并不需要用@Inject标记:
第二步:我们需要定义一个@Module来管理这些依赖类的实例:
第三步:定义一个@Component,它指向上面定义的@Module
第四步:在目标类中进行依赖注入
这里注入的方式有两种,一种是像上面这样的,它适合于PersonDataModule中只有一个无参的构造方法,否则我们需要这样调用:
五、初始化依赖实例的步骤
-
查找
Module中是否存在创建该类型的方法(即@Component标记的接口中包含了@Module标记的Module类,如果没有则直接查找@Inject对应的构造方法)。 -
如果存在创建类方法,则查看该方法是否有参数
-
如果不存在参数,直接初始化该类的实例,一次依赖注入到此结束。
-
如果存在参数,则从步骤1开始初始化每个参数。
-
如果不存在创建类方法,则查找该类型的类中有
@Inject标记的构造方法,查看构造方法是否有参数: -
如果不存在参数,则直接初始化该类实例,一次依赖注入到此结束。
-
如果存在参数,则从步骤1开始初始化每个参数。
六、@Qualifier限定符
在Dagger中,有一个已经定义好的限定符,@Name,下面我们也自己定义一个限定符:
第一步:和前面类似,我们先定义一个需要实例化的依赖类:
第二步:我定义一个DataModule,和前面不同的是,在它的provideXXX方法的注解中,我们添加了@Name(xxx)和自定义的注解PeopleThreePeople:
第三步:定义Component
第四步:在目标类中进行依赖注入,在提供@Inject注解时,我们还需要声明和PeopleDataModule中对应的限定符,这样Dagger就知道该用那个函数来生成目标类中的依赖类实例:
七、@Scope
@Scope的作用主要是在组织Component和Module的时候起到一个提醒和管理的作用,在Dagger中,有一个默认的作用域@Singleton。@Scope的作用是:Dagger2可以通过自定义Scope注解,来限定通过Module和Inject方式创建的类的实例的生命周期能够与目标类的生命周期相同。Scope的真正作用在与Component的组织:
- 更好的管理
Component之间的组织方式,不管是依赖方式还是包含方式,都有必要用自定的Scope注解标注这些Component,而且编译器会检查有依赖关系或包含关系的Component,若发现有Component没有用自定义Scope注解,则会报错。 - 更好地管理
Component与Module之间地关系,编译器会检查Component管理的Module,若发现Component的自定义Scope注解与Module中的标注创建类实例方法的注解不一样,就会报错。 - 提高程序的可读性。
下面是一个使用@Singleton的例子:
第一步:定义需要实例化的类:
第二步:定义DataModule,在它的provideXXX方法,提供了@Singletion注解:
第三步:定义Component,和前面不同的是,需要给这个Component添加@Singleton注解:
第四步:在目标类中进行依赖注入,每次启动Activity的时候,我们可以发现打印出来的hash值都是相同的:
八、组织Component
Component有三种组织方式:
- 依赖:一个
Component依赖一个或多个Component,采用的是@Component的dependencies属性。 - 包含:这里就用到了
@SubComponent注解,用它来标记接口或者抽象类,表示它可以被包干。一个Component可以包含一个或多个Component,而且被包含的Component还可以继续包含其它的Component。 - 继承:用一个
Component继承另外一个Component。
九、Google官方框架分析
下面是Google官方框架的目录结构:
可以看出,它把每个界面都作为一个独立的包(addedittask、statistics、taskdetail、tasks),而数据、依赖类是其它的两个包(data、util),我们先从ToDoApplication开始分析:
在ToDoApplication中,我们实例化了一个变量TasksRepositoryComponent,它相当于是项目中所有其它Component的管理者,它被声明为@Singleton的,即在App的生命周期中只存在一个,同时用@Component表明它和TaskRepositoyModule、ApplicationModule这两个Module关联。
TaskRpositotyModule提供了两种类型的数据源对象,它们是用@Local、@Remote来区分的:
接下来再看一下ApplicationModule
下面我们用一个比较简单的界面来看一下TasksRepositoryComponent是怎么和其它的Component关联起来的,首先看StatisticsActivity:
原文链接:https://www.jianshu.com/p/5285f48b6336
阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680
浙公网安备 33010602011771号