Dagger2从基础到高级(二)

github地址
文档

三、模块化实现

接着前一篇文章的讲解,我们了解到ApiSrvice这个类中有register()方法,一般我们网络请求用的是Retrofit和OkHttp3,所以我们需要创建OkHttpClient,在register中调用请求网络的方法:
>

这里写图片描述

(测试代码数据没意义)

那么OkHttpClient怎么实例化呢?
我们可以在构造方法中传入:

这里写图片描述

这样写并没有实例化,所以我们可以在Module中提供返回值为OkHttpClient的方法,

这里写图片描述

这样写是可以的,但是我们在开发中会多次利用OkHttpClient对象,不同的Activity都会用到,我们不能每次都要在不同的Module中返回该对象,这样性能开销过大。
我们可以重新创建一个HttpModule类,这个类专门提供和Http相关的,这样就把某些公共的东西抽离了出来,实现模块化,我们还可以创建一个AppModule来提供Context对象,当然Context对象不是我们能够New出来的,是系统提供的,但是原理一样,我们可以把它抽出来写成一个个Module.

这里写图片描述

我们创建了该对象,那么怎么用呢?
(我们先显示的在UserModule中提供ApiService对象,这样看起来直观一些,所以讲ApiService构造方法上的@Inject去掉)

这里写图片描述

OkHttpClient对象的提供在HttpModule中,那么我们怎么引用呢?

有几种方式:

第一种(Module依赖Module):在UserModule上添加@Module(includes = HttpModule.class)从而引入HttpModule

第二种(Component依赖Module):在UserComponent上添加@Component(modules = {UserModule.class, HttpModule.class})引入HttpModule

第三种(Component依赖Component):我们可以再写一个HttpComponent并在其上添加@Component(modules = HttpModule.class),然后在UserComponent上添加@Component(modules = {UserModule.class},dependencies = HttpComponent.class)

四、创建和区分不同实例

在开发中可能会遇到测试环境和线上环境,所以就分两种不同情况,在MainActivity中,我们之创建一个ApiService,所以现在我们要创建两个ApiService对象
>

这里写图片描述

但是我们在UserModule中提供一种方法来提供ApiService对象,那么Dagger2怎么区分两个对象呢?

在MainActivity中创建几个ApiService对象,就需要在UserModule中创建几个方法返回该对象实例,但是在方法上还需要加上@Name(“”)加以区分:

这里写图片描述

并且在MainActivity中也要加上@Name

这里写图片描述

在Module中提供对象的时候添加一个名字,在Activity中使用的时候添加一个名字

还有另一种方法区分不同对象,就是自己创建一个Anotation(注释)

我们先创建测试和线上的两个Anotation

这里写图片描述

这里写图片描述

在Module中提供对象,在Activity中使用对象

这里写图片描述

这里写图片描述

**@Qualifier: 要作用是用来区分不同对象实例
@Named 其实是@Qualifier的一种实现**

五、Singleton单例讲解

在MainActivity中使用的对象如果要保证是单例模式的话,就必须在Module中提供对象的方法上加上@Singleton,并且在对应的Component类上也要加上@Singleton,否则会报错

这里写图片描述

这里写图片描述

以上是单例模式的讲解

我们再重新创建一个LoginActivity用来登录,注入UserManager对象,并调用login()方法(方法自行添加),这时我们遇到一个问题,发现注入不了UserManager对象,因为UserComponent里面inject()方法只接受一个MainActivity对象,那么我们怎么在LoginActivity中注入呢?我们能否将inject方法的参数改成AppCompatActivity?发现改完之后编译不会报错,但是运行的时候还是报错,usermanager对象为空,为什么会这样呢?

原因很简单:这其实是dagger2的深坑!!!!!

注意事项

……
1. componet 的 inject 方法接收父类型参数,而调用时传入的是子类型对象则无法注入
2. component关联的modules中不能有重复的provide
3. module 的 provide 方法使用了 scope ,那么 component 就必须使用同一个注解
4. module 的 provide 方法没有使用 scope ,那么 component 和 module 是否加注解都无关紧要,可以通过编译
5.component的dependencies与component自身的scope不能相同,即组件之间的scope不同
6.Singleton的组件不能依赖其他scope的组件,只能其他scope的组件依赖Singleton的组件(@Singleton级别最高Application级别的)
7.没有scope的component不能依赖有scope的component
8.一个component不能同时有多个scope(Subcomponent除外)
9.@Singleton 的生命周期依附于component,同一个module provide singleton ,不同component 也是不一样

难道我们还需要重新写一个Component?于是我们再创建一个
(因为UserModule中有个方法加了@Singleton,所以这里也要添加)
这里写图片描述

发现确实可以,但是有个新的情况出现,我们明明加了@Sington,但是在MainActivity和LoginActivity打印出来的OkHttpClient对象怎么不是同一个,没有实现单例模式?这是什么情况?

请看注意事项第九条:dagger2中的单例模式和java中的单例模式不一样,这里虽然两个Component都连接着UserModule但是Component却不是同一个。

那我们怎么办呢?
……………………..

六、自定义Scope

我们重新定义一个类AppComponent,用于关联AppModule,抽出提供OkHttpClient对象方法:
>

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

编译之后发现报错

这里写图片描述

原因是:component的dependencies与component自身的scope不能相同,即组件之间的scope不同(AppComponent已经加了@Singleton,那么LoginComponent和UserComponent依赖AppComponent就不能再添加相同的@Singleton)

Dagger2只提供一种Scope,所以我们要自己定义Scope

自定义Scope:

@Scope :注明是Scope

@Documented :标记在文档

@Retention(RUNTIME) :运行时级别

推荐文章:http://frogermcs.github.io/dependency-injection-with-dagger-2-custom-scopes/

我们新创建一个类ActivityScope

这里写图片描述

然后在LoginComponent和UserComponent加上

这里写图片描述

这里写图片描述

重新编译发现还是出错:

这里写图片描述

OkHttpClient对象并不能提供

我们还必须在AppComponent中添加一个抽象方法
将AppModule中的OkHttpClient暴露出来,以便于其他依赖于AppComponent的Component调用

这里写图片描述

重新编译发现没有错误,在MainActivity中我们发现了一个新的方法(AppComponent):

这里写图片描述

为什么会出现AppComponent呢,因为我们在UserComponent中引用了AppComponent,@Singleton是Application级别的,所以我们在Application中创建比较合适,当Application销毁掉,那么AppModule中的单例OkHttpClient也会一并销毁

这里写图片描述

在Application中创建AppComponent,然后在MainActivity和LoginActivity中使用:

这里写图片描述

这里写图片描述

这样就实现了单例模式,用@Singleton依附与Application的生命周期,而不是Activity的生命周期

七、SubCompnet和Lazy与Provider

Component和Component之间的引用除了@Component(modules = {},dependencies = OtherComponent.class)还可以通过SubCompnet,具体用法如下:

新建一个父组件FComponent和一个子组件CComponent

这里写图片描述

通过@Subcomponent()
这里写图片描述

这里写图片描述

这样就实现了父组件关联到子组件

如果在UtilModule里提供Gson的方法中需要传入参数:

这里写图片描述

如果UtilModule中没有提供OkHttpClient的方法,那么他会主动找到父组件关联的AppModule找有没有提供OkHttpClient的方法。

不过有两点需要注意的地方:

1、Subcomponent同时具备两种不同生命周期的scope, SubComponent具备了父Component拥有的Scope,也具备了自己的Scope。 如果父组件的生命周期是@Singleton,那么子组件就不能定义成@Singleton

2、SubComponent的Scope范围小于父Component

这里写图片描述

这里写图片描述

编译之后发现报错:

这里写图片描述

原因是因为在UserComponent中已经提供注入到MainActivity了,所以在CComponent中再重复注入到MainActivity会出现错误,所以这也是Dagger2的坑………

所以我们必须重新定义一个Activity,再注入进去

这里写图片描述

Dagger2Lazy与Provider

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值