相关阅读:
吊炸天!74款APP完整源码!
Android架构剖析之一个APP从启动到主页面显示经历了哪些过程?
阿里两个最新Android开源库,Android开发者的福音
来源:http://blog.spinytech.com/2016/12/28/android_modularization/
关于
模块化(组件化)
这个问题,我想每个开发者可能都认真的思考过。随着项目的开发,
业务不断壮大,业务模块越来越多,各个模块间相互引用,耦合越来越严重
,同时有些项目(比如我们公司)还伴随着子应用单独包装推广,影子应用单独发布等等需求,重新调整架构迫在眉睫。今天,我们就来聊聊模块化(组件化),这篇文章同时也是我这几年,对项目架构的理解。
最初的超小型项目
当我们最开始做Android项目的时候,大多数人都是没考虑项目架构的,我们先上一张图。
2012年开发的一个小项目
这个分包结构有没有很熟悉,各种组件都码在一个包里,完全没有层级结构,
业务、界面、逻辑都耦合在一起
。这是我12年底刚开始入门Android的时候开发的一个小项目,半年后,来了个小伙伴,然后我们一起开发,然后天天因为谁修改了谁的代码
打的不可开交
。
刚刚重构之后的架构
做了几件微小的工作之后
可以看到,随着几个版本业务的增加,各个业务某块之间耦合愈发严重,导致代码很难维护,更新,更别说写测试代码了。虽然后期引入统一广播系统,一定程度改善了模块间相互引用的问题,但是局限性和耦合性还是很高,没办法根治这个问题。这个架构做到最后,
扩展性和可维护性都是很差
,并且
难以测试
,所以最终被历史的进程所抛弃。
路由架构
通过上图可以看到,我们在最基础的Common库中,创建了一个路由
Router
,中间有n个模块
Module
,这个
Module
实际上就是Android Studio中的module,这些
Module
都是
Android Library Module
,最上面的Module Main是
可运行的Android Application Module
。
这几个
Module
都引用了Common库,同时Main Module还引用了A、B、N这几个
Module
,经过这样的处理之后,
所有的
Module
之间的相互调用就都消失了,耦合性降低,所有的通信统一都交给Router来处理分发,而注册工作则交由Main Module去进行初始化
。这个架构思想其实和Binder的思想很类似,采用C/S模式,模块之间隔离,数据通过共享区域进行传递。模块与模块之间只暴露对外开放的Action,所以也具备
面向接口编程思想
。
图中的红色矩形代表的是行动
Action
,
Action
是具体的执行类,其内部的
invoke方法是具体执行的代码逻辑
。如果涉及到
并发操作
的话,可以在
invoke方法内加入锁,或者直接在invoke方法上加上synchronized描述
。
图中的黄色矩形代表的是供应商
Provider
,每个
Provider
中包含1个或多个
Action
,其内部的数据结构以
HashMap
来存储Action。
首先HashMap查询的时间复杂度是O(1),符合我们对调用速度上的要求,其次,由于我们是统一进行注册,所以在写入时并不存在并发线程并发问题,在读取时,并发问题则交由Action的invoke去具体处理。
在每一个
Module
内都会有1个或多个供应商
Provider
(如果不包含
Provider
,那么这个
Module
将无法为其他
Module
提供服务)。
途中蓝色矩形代表的是路由
Router
,每个
Router
中包含多个
Provider
,其内部的数据结构也是以
HashMap
来存储
Provider
,原理也和
Provider
是一样的。之所以用了两次HashMap,有两点原因,一个是因为这样做,
不容易导致
Action
的重名
,另一个是因为在注册的时候,只注册
Provider
会
减少注册代码,更易读
。并且由于HashMap的查询时间复杂度是O(1),所以两次查找不会浪费太多时间。当查找不到对应
Action
的时候,Router会生成一个
ErrorAction
,会告之调用者没有找到对应的
Action
,由调用者来决定接下来如何处理。
Router时序图
-
任意代码创建一个
RouterRequest
,包含
Provider
和
Action
信息,向
Router
进行请求。
-
Router
接到请求,通过
RouterRequest
的
Provider
信息,在内部的HashMap中查找对应的
Provider
。
-
Provider
接到请求,在内部的HashMap中查找到对应的
Action
信息。
-
Action
调用invoke方法。
-
返回invoke方法生成的
ActionResult
。
-
将
Result
封装成
RouterResponse
,返回给调用者。
取消对Module N的依赖
如图所示,我们取消了对
Module N
的依赖,整体应用依然可以稳定运行,遇到调用
Module N
的地方,会返回Not Found提示,实际开发中可以根据需求做具体的处理。
测试Module A
《Android多进程使用场景》
中也做过介绍,大体优点有这么几个:
-
提高各个进程的稳定性,单一进程崩溃后不影响整个程序。
-
对于内存的时候更可控,可以通过手工释放进程,达到内存优化目的。
-
基于独立的JVM,各个模块可以充分解耦。
-
只保留daemon进程的情况下,会使应用存活时间更长,不容易被回收掉。
路由连接架构
如图所示,竖直方向上,每一列,代表一个进程,通过虚线隔开,分别有Process WideRouter、Process Main、Process A、···、Process N这些进程。浅黄色的代表
WideRouter
,深黄色的代表
WideRouter
的守护Service。浅蓝色的代表每个进程的
LocalRouter
,深蓝色的代表每个
LocalRouter
的守护Service。
WideRouter
通过AIDL与每个进程
LocalRouter
的守护Service绑定到一起,每个
LocalRouter