Android开发Android-Rxjava&retrofit&daggerAndroid技术知识

dagger2从入门到放弃-多模块项目下dagger的使用

2018-11-01  本文已影响7人  sunhapper

在只有一个Module的Android Project中,dagger2的使用相对来说是比较顺畅的,但是事实上现在基本上不会存在只有一个Module的项目了。

一个最底层的Module,上层多个功能模块,再上层多个业务模块,再上层一个app模块去组织业务和功能,这是大部分android项目的结构,而在这种结构中dagger2的使用就会有一些不太顺手了

场景构建

先确定一下dagger需要在多模块中实现什么样的场景

这基本是比较简单的场景了

正常思路下的使用

在介绍Component的继承体系时提到过只有使用Component的dependencies属性指定父Component的方式才能在子Component指定父Component,这是唯一的从上到下的确定依赖关系的方式

很自然的会想到在每个模块拥有一个Component,上层的Component依赖下层Component

BaseLib

这三步实现起来比较自然

ApiLib

第三步问题来了,在什么时机去创建一个ApiComponent实例呢

作为一个处于中间层级的库,ApiLib并不适合拥有自己的Application类,BaseLib在下层访问不到ApiLib的代码,所以只能将ApiComponent的创建逻辑放到上层的App模块中

这样会造成本来ApiLib不需要上层去进行初始化,而现在必须依赖上层初始化才能正常使用。而且现在设定的场景是一种比较简单的情况,实际项目中的模块依赖会更加复杂,所有的模块的Component的初始化逻辑都放App模块中需要App模块的开发者了解所有模块Component的依赖关系,这不现实也不合理

不过有问题暂时先搁置,想个办法先跑起来

App模块

还是没跑起来

按照上面的步骤应该可以正常运行才对,但是天不遂人愿,还是有地方姿势不对

之前的文章中提到过使用dependencies指定依赖的情况下,父Component需要显式暴露提供依赖的方法,而且不具有传递性,只能为下一级提供依赖而不能为下下级提供依赖

所以ApiComponent只暴露获取OkHttp和Retrofit的方法还不够,还需要暴露获取Gson的方法

添加了获取Gson的方法之后代码终于可以正常运行了

方案评价

通过上面的介绍,大家应该发现了这种方案在多模块的项目中dagger使用不是那么顺手

优化

前两个痛点的问题在于Application的创建时机只有最上层的模块才知道,仔细分析代码发现事实上这个时机并不重要,主要目的是要给Component提供一个Application实例同时保持和Application相同的生命周期

这样修改之后,库的根Component的管理都变成了在库中完成,App模块就不需要关注下层库中Component的组织了;当然如果App模块也要用依赖注入的方式使用下层库中dagger提供的依赖,那还是需要处理自己得Component和要用到的库的Component的依赖关系,不过这样是用到哪个处理哪个,而不是之前的不管用不用都要处理所有依赖关系

第三个痛点仔细考虑了一下发现可以在底层的模块中的提供一个接口,暴露获取依赖的方法,上层Component只用继承这样的接口,而添加依赖只用在接口中增加方法,不用在每个Component中一个个添加

最后一个痛点暂时还没发现好的解决方式

上层Application中处理所有依赖

上面的方案经过优化其实基本可用了,但是dagger使用起来是能用注解的地方都用注解才好,不能跨库使用dagger.android还是有些麻烦的

所以对自己整体把控的项目,对dagger使用变成了在App模块的Component中进行所有依赖的组织

先看看怎么实现

对Module中providerXXX方法的Scope进行统一

之前是用多个Component去组织,所以需要多个Scope进行标记,现在是一个Component,所以只能使用一个Scope进行标记

在每个模块中定义一个Module,将Application级别的依赖include进去

这一步是为了简化App模块的工作,一个库的Application级别的Module自己去组织,App只需要包含一个Module,库中添加新的Module也不需要上层的改动

@Module(includes = {ApiServiceModule.class, OrmModule.class})
public class ApiCollectionModule {
}

将所有的库提供的Module包含到AppComponent中

@AppScope
@Component(modules = {AppModule.class,
        BaseAppCollectionModule.class,
        ApiCollectionModule.class,
        MvvmCollectionModule.class})
public interface AppComponent {
...
}

如何将依赖注入到库中的对象

上面的做法完成了将依赖注入到App模块的准备工作,但是库中的类怎么实现依赖注入呢?

这里提供两种方式

dagger.android的方式和之前基本没啥区别,所以主要说下SubComponent的方式

与上个方案的比较

上个方案实际每个库的依赖集合都是独立的,每个库的依赖集合包括当前库提供的依赖和下层库暴露的依赖
而当前方案的实际是只有一个总的依赖集合,当然因为dagger是编译期的依赖注入框架,所以即使注入时所有的依赖都有,但是也无法用反射这样的方式为组件提供上层的依赖

不过因为是一个整体,所以组织起来比第一种方案方便一些

优点

缺点

上层的App模块也必须依赖dagger

总结

dagger在多模块的项目中的使用或者说在Library中的使用确实会有很多的不顺手的地方,不同的方案有利有弊,要看具体的场景进行选择

只在App模块使用dagger

这种引入dagger的方式是代价最小的,可以渐进的去迭代,但是有些Module是应该放在库中公用的,有些Component的依赖逻辑也是可以公用的,现在只存在于App模块中导致复用度不高

这种情况适合只负责上层业务开发的情况

只在库模块中使用dagger

如果要开发一个独立的库,想用dagger的话,只能用第一种方案了,因为库是独立的;如果需要Application实例的话还需要指定Application onCreate时传入Application实例等初始化操作

个人不建议在独立的库中使用dagger

当然如果你的库并不需要和Application作为依赖同时没有使用Multibindings来解耦的场景也是可以用dagger的

在全局使用dagger

这种情况才是用起来最爽的,dagger有一种用的地方越多用起来越顺手的特性,而这种情况适合文章中介绍的第二种方案

当然真实的项目千差万别,所以还是需要自己衡量项目中到底要不要使用要怎样使用dagger

相关文章

dagger2从入门到放弃-概念
dagger2从入门到放弃-最基础的用法介绍
dagger2从入门到放弃-Component的继承体系、局部单例
dagger2从入门到放弃-ActivityMultibindings
dagger2从入门到放弃-dagger.android
dagger2从入门到放弃-其他用法
dagger2从入门到放弃-多模块项目下dagger的使用
dagger2从入门到放弃-为何放弃

示例代码

DaggerInAction
欢迎star
master分支上最新的代码可能会比当前文章的示例代码稍微复杂点,提交记录里包含了每一步的迭代过程,可以顺藤摸瓜

上一篇下一篇

猜你喜欢

热点阅读