专栏名称: 开发者全社区
分享和推送Java/Android方向的技术和文章,让你成为这方面的大牛,让你每天都成长一点。同时,我们也会邀请BAT的大牛分享原创!
目录
相关文章推荐
开发者全社区  ·  什么叫专业捞女!? ·  15 小时前  
开发者全社区  ·  春节变成了出轨高发期...... ·  20 小时前  
开发者全社区  ·  华为夫妇一夜回到解放前 ·  昨天  
开发者全社区  ·  航空公司门口惊现小纸条 ·  2 天前  
51好读  ›  专栏  ›  开发者全社区

Android架构之构建模块化、多进程的APP实践

开发者全社区  · 公众号  · android  · 2017-04-02 08:51

正文

相关阅读:

吊炸天!74款APP完整源码!

Android架构剖析之一个APP从启动到主页面显示经历了哪些过程?

阿里两个最新Android开源库,Android开发者的福音

来源:http://blog.spinytech.com/2016/12/28/android_modularization/

关于 模块化(组件化) 这个问题,我想每个开发者可能都认真的思考过。随着项目的开发, 业务不断壮大,业务模块越来越多,各个模块间相互引用,耦合越来越严重 ,同时有些项目(比如我们公司)还伴随着子应用单独包装推广,影子应用单独发布等等需求,重新调整架构迫在眉睫。今天,我们就来聊聊模块化(组件化),这篇文章同时也是我这几年,对项目架构的理解。


最初的超小型项目

当我们最开始做Android项目的时候,大多数人都是没考虑项目架构的,我们先上一张图。

2012年开发的一个小项目

这个分包结构有没有很熟悉,各种组件都码在一个包里,完全没有层级结构, 业务、界面、逻辑都耦合在一起 。这是我12年底刚开始入门Android的时候开发的一个小项目,半年后,来了个小伙伴,然后我们一起开发,然后天天因为谁修改了谁的代码 打的不可开交

架构改进,小型项目

再后来开发App,人员比之前多了,所以不能按照以前那样了,必须得重构。于是我把公用的代码提取出来制作成 SDK基础库 ,把单独的功能封装成 Library包 ,不同业务通过分包结构分到不同 module 下,组内每人开发自己的module。刚开始都还轻松加愉快,并行开发啥的,一片融洽的场景,如下图。

刚刚重构之后的架构

做了几件微小的工作之后

可以看到,随着几个版本业务的增加,各个业务某块之间耦合愈发严重,导致代码很难维护,更新,更别说写测试代码了。虽然后期引入统一广播系统,一定程度改善了模块间相互引用的问题,但是局限性和耦合性还是很高,没办法根治这个问题。这个架构做到最后, 扩展性和可维护性都是很差 ,并且 难以测试 ,所以最终被历史的进程所抛弃。

中小型项目,路由架构


实现原理

我们先来看下路由架构图

路由架构

通过上图可以看到,我们在最基础的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调用的具体流程是这样的:

Router时序图

  1. 任意代码创建一个 RouterRequest ,包含 Provider Action 信息,向 Router 进行请求。

  2. Router 接到请求,通过 RouterRequest Provider 信息,在内部的HashMap中查找对应的 Provider

  3. Provider 接到请求,在内部的HashMap中查找到对应的 Action 信息。

  4. Action 调用invoke方法。

  5. 返回invoke方法生成的 ActionResult

  6. Result 封装成 RouterResponse ,返回给调用者。

耦合降低

所有的 Module 之间的相互依赖没有了,我们可以在主app中,取消任意的 Module 引用而不影响整体App的编译及运行。

取消对Module N的依赖

如图所示,我们取消了对 Module N 的依赖,整体应用依然可以稳定运行,遇到调用 Module N 的地方,会返回Not Found提示,实际开发中可以根据需求做具体的处理。

可测试性增强

由于每个 Module 并不依赖其他的 Module ,所以在开发过程中,我们只针对自己的模块进行开发,并可以建一个测试App来进行白盒测试。

测试Module A

复用性增强

关于复用性这块。作者所处的行业是招商投资这块,这个行业需要围绕主业务开发很多影子APP,将覆盖面扩大(有点类似58->58租房、58招聘,美团->美团外卖等)。这个时候,这个架构的复用性就体现出来了,我们可以把业务进行拆分,然后写一个包装App,就可以生成一个独立的影子APP,这个影子APP用到哪些 Module 就引用哪些就可以了,开发迅速,并且后期 Module 业务有变化,也不用更改所有的代码,减少了代码的复制。比如我们就曾经把IM模块和投资咨询模块单独拿出来,写了一些界面和样式,就生成了“招商经纪人”App。

支持并行开发

整套架构很类似Git的Branch思想,基于主线,分支单独开发,最后再回归主线这种思路。这里只是思路和branch相似,实际的开发过程中,我们每个module可以是一个branch,也可以是一个仓库。每个模块都需要自己有单独的版本控制,便于问题管理及溯源。主项目对各个模块的引用可以是直接引用,也可以是导出aar引用,或者是上传JCenter Maven等等方式。不过思路是统一的:继承公共->独立开发->主线合并。

多进程思考,中型项目

随着项目的不断扩大,App在运行时的内存消耗也在不断增加,而且有时线上的BUG也会导致整体崩溃。为了保证良好的用户体验,减少对系统资源的消耗,我们开始考虑采取多进程重新架构程序,通过按需加载,及时释放,达到优化的目的。

多进程优势

多进程的优点和使用场景,之前在 《Android多进程使用场景》 中也做过介绍,大体优点有这么几个:

  • 提高各个进程的稳定性,单一进程崩溃后不影响整个程序。

  • 对于内存的时候更可控,可以通过手工释放进程,达到内存优化目的。

  • 基于独立的JVM,各个模块可以充分解耦。

  • 只保留daemon进程的情况下,会使应用存活时间更长,不容易被回收掉。

潜在问题

但是启用多进程,那就意味着Router系统的失效。 Router是JVM级别的单例模式,并不支持跨进程访问 。也就是说,你的后台进程的所有 Provider Action ,是注册给后台Router的。当你在前台进程调用的时候,根本调用不到其他进程的 Action

解决方案

其实解决的方法也并不复杂。原来的路由系统还可以继续使用,我们可以 把整套架构想象成互联网 ,现在多个进程有多个路由,我们只需要把多个路由连接到一起,那么整个路由系统还是可以正常运行的。所以我们把原有的路由 Router 称之为本地路由 LocalRouter ,现在,我们需要提供一个IPS、DNS供应商,那就创建一个进程,该进程的作用就是注册路由,链接路由,转发报文,我们称之为广域路由 WideRouter

我们先来看下路由连接架构图

路由连接架构

如图所示,竖直方向上,每一列,代表一个进程,通过虚线隔开,分别有Process WideRouter、Process Main、Process A、···、Process N这些进程。浅黄色的代表 WideRouter ,深黄色的代表 WideRouter 的守护Service。浅蓝色的代表每个进程的 LocalRouter ,深蓝色的代表每个 LocalRouter 的守护Service。 WideRouter 通过AIDL与每个进程 LocalRouter 的守护Service绑定到一起,每个 LocalRouter







请到「今天看啥」查看全文


推荐文章
开发者全社区  ·  什么叫专业捞女!?
15 小时前
开发者全社区  ·  春节变成了出轨高发期......
20 小时前
开发者全社区  ·  华为夫妇一夜回到解放前
昨天
开发者全社区  ·  航空公司门口惊现小纸条
2 天前