Android 组件化之通信(多模块,多进程)

Tor81I 7年前
   <h2>1. 引子</h2>    <p>写这篇文章主要是有两个原因:</p>    <ol>     <li> <p>之前写过一篇 Android组件化开发实践 ,组件化最直接的表现形式就是工程中包含了多个业务Module,代码要解耦,但是业务间要解耦几乎是不可能的,于是就要涉及到业务间的通信,表现在代码上就是Module间通信。其实在文章提到的ActivityRouter就是模块间通信很好的一个library,但是其主要作为Activity Router来使用,传递数据的能力有限(当然稍微改造一下代码,交换数据还是很容易解决的)。</p> </li>     <li> <p>最近看到了 Spiny 同学的 Android架构思考(模块化、多进程) ,写的非常好,在掘金上分享后得到了不少同学的点赞。尤其文中提到的多进程方案,自己也非常感兴趣,于是就去看了源码,很有想象力,但是在使用时个人感觉也有一些小问题,于是就fork了一份代码,开始对代码进行了一些修改,本文中将主要介绍一下这些修改。</p> </li>    </ol>    <h2>2. 模块间通信原理</h2>    <p>我们先来看一下架构图,图片来源于 http://blog.spinytech.com/2016/12/28/android_modularization/</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/efc612f9469d40797fd25a6b84a3127e.jpg"></p>    <p>从A、B到N的多个Module都引用了Common库,同时Main Module还引用了A、B、N这几个Module,经过这样的处理之后,所有的Module之间的相互调用就都消失了,耦合性降低,所有的通信统一都交给Router来处理分发,而注册工作则交由Main Module去进行初始化。这个架构思想其实和Binder的思想很类似,采用C/S模式,模块之间隔离,数据通过共享区域进行传递。模块与模块之间只暴露对外开放的Action,所以也具备面向接口编程思想。</p>    <p>架构图中每个红色的Action都是可以提供的服务,而Provider是一个服务的集合,每个Module中可以有一个或者多个Provider,当程序开始执行时,module会把Provider注册到Router Module。Action服务请求流程如下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a17754aed82eee14d5124a5ff8dfd8b1.jpg"></p>    <ol>     <li> <p>任意代码创建一个RouterRequest,包含Provider和Action信息,向Router进行请求。</p> </li>     <li> <p>Router接到请求,通过RouterRequest的Provider信息,在内部的HashMap中查找对应的Provider。</p> </li>     <li> <p>Provider接到请求,在内部的HashMap中查找到对应的Action信息。</p> </li>     <li> <p>Action调用invoke方法。</p> </li>     <li> <p>返回invoke方法生成的ActionResult。</p> </li>     <li> <p>将Result封装成RouterResponse,返回给调用者。</p> </li>    </ol>    <h2>3. 多进程架构模块通信原理</h2>    <p>还是先来看下架构图:</p>    <p><img src="https://simg.open-open.com/show/104db21a9ac854b5d9913e4605557725.jpg"></p>    <p>Router是JVM级别的单例模式,并不支持跨进程访问。也就是说,你的后台进程的所有Provider、Action,是注册给后台Router的。当你在前台进程调用的时候,根本调用不到其他进程的Action。</p>    <p>解决方案是单独提取一个Wide Router模块,所有的Local Route与Wide Router通过进程间通信的方式建立连接,Action请求如果在Local Router中找不到时,则通过WideRouter与其它进程建立连接,WideRouter充当局域网之间的路由。一次跨进程的Action请求如下图所示:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/49f08ae3af204c5c970dc347c25dbcbf.jpg"></p>    <p>以上的内容主要来自于 Android架构思考(模块化、多进程) ,目前也已经有了相应的demo,大家可以去尝试一下,体验多进程App的乐趣。</p>    <h2>4. 可以改进的地方</h2>    <p>关于ModularizationArchitecture的使用,有相应的文档: ModularizationArchitecture 使用教程 。个人在使用后感觉又一些不太方便的地方,主要有三点:</p>    <h3>4.1 通信数据格式问题</h3>    <p>目前发起请求时传递的数据是 RouterRequest ,接收的数据是 RouterReponse ,两种类型的数据中包含的只有字符串数据,在实际的进程间通信时传递的数据也是字符串(代码中转为了json数据)。这种方式不能传递自定义的数据,在数据使用时也要手动解析字符串,比较繁琐。</p>    <p>其实Android为进程间通信提供了Parcelable,可以通过这种方式非常方便地传递数据。</p>    <h3>4.2 线程切换问题</h3>    <p>demo中采用新建线程的方式请求异步数据:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/d2ae7be1d4d02b901996ec02fd22b6df.jpg"></p>    <p>代码看起来不够优雅,采用Rxjava 的方式会使得线程切换更加优雅,Router模块可以使用Rxjava的方式返回结果。</p>    <h3>4.3 Provider、Action 注册问题</h3>    <p>代码中Provider、Action需要手动注册,如果增加一个Action,需要有多个地方进行变动,这里可以采用apt的方式来自动进行注册。</p>    <h2>5. 解决方案</h2>    <p>针对以上的三个问题,对代码进行来部分修改,如下:</p>    <h3>5.1 进程间通信数据格式的修改</h3>    <p>请求数据 RouterResquest 和返回数据 MaActionResult 分别实现了Parcelable接口,并且分别提供了两个可以自定义的成员变量 requestObject 和 result ,用户在建立请求数据 RouterResquest 和返回数据 MaActionResult 可以把自定义的数据传递进去,需要注意的是传递的自定义类型也要实现Parcelable接口。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/d9e7c3f6af74d0d9022ed0f56dacf926.jpg"></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/cc60797ba3401b0452f562afb3451809.jpg"></p>    <h3>5.2 Provider、Action自动生成</h3>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a6c0d49f6debe72288a1d0db8f741707.jpg"></p>    <h3>5.3 Rxjava 的引入</h3>    <p>引入Rxjava之后,修改LocalRoute的route方法,使之返回 Observable<MaActionResult> ,在调用时可以非常方便地使用Rxjava切换线程:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/602bc9c420621734ecbfb275b6eda3aa.jpg"></p>    <h2>6. 使用教程</h2>    <p>项目地址: https://github.com/wutongke/ModularizationArchitecture</p>    <h3>6.1 在项目中集成</h3>    <p>6.1.1 在project的build.gradle中设置maven地址:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/382a5fccc7048e27378854d7317e5575.png"></p>    <p>dependencies块中支持apt:</p>    <p>6.1.2 所有Module中配置apt插件:</p>    <p>dependencies块中设置:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/74f0ef3aa46fc3cdae9c3b022018cab0.png"></p>    <h3>6.2 创建自定义Application</h3>    <p>6.2.1 实际Application</p>    <p>我们知道一个app中只有一个Application,所以在主Module中定义Application,然后在其它模块中根据需要实现逻辑Application即可,然后启动时注册逻辑Application,即可管理其生命周期:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/2b41ad4ca5f9b1563680afa98baeaca6.jpg"></p>    <p>当然这个自定义的Application需要注册到manifest文件中。</p>    <p>使用多进程提供服务的模块需要继承 LocalRouterConnectService ,并且在manifest中注册服务:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/68033b598e6f285ab77795a00e187111.jpg"></p>    <p>6.2.2 逻辑Application</p>    <p>逻辑Application通过继承BaseApplicationLogic,实现相应的方法即可被回调。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/2efb1a01fd6dc984579189167058cde6.jpg"></p>    <h3>6.3 自定义Provider和Action</h3>    <p>定义Provider</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/f86da660089eaab1e6ec3530cf5a3846.jpg"></p>    <p>定义Action</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/82fb4cf96b1c20afc9c5bc2c19f82962.jpg"></p>    <p>可以看到定义Provider和Action时分别使用了 @Provider 和 @Action 注解,这样可以在程序编译时完成自动的注册,不需要手动注册到Router了。</p>    <p>其中 @Provider 需要设置进程名字, @Action 需要设置进程名字和注册到的Provider名字:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/28d239e56b0329be66bcc38ee808103c.jpg"></p>    <h3>6. 4 调用Action</h3>    <p>6.4.1 建立Action调用</p>    <p>首先需求建立一个请求 RouterRequest ,说明要请求的内容:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/c56286fa5575454675a5a7d6136253f9.jpg"></p>    <p>可以通过RouterRequestUtil的obtain方法快速建立请求,上例中请求的Action位于”com.spinytech.maindemo:music”进程,Provider是”music”,Action是”play”,并且传递了相应的参数new Song(“see you”)。</p>    <p>然后使用Rxjava的方式请求Action:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/80dc30bd84a2acecdef8571aa77fe010.jpg"></p>    <p>6.4.2 处理请求</p>    <p>在music模块中处理刚刚发出的请求,6.3中定义的Provider和Action其实就是处理6.4.1中的请求的,并且返回了 MaActionResult :</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/87929fa7ee9ba8c09bf8046e21b3be14.jpg"></p>    <h2>7. 总结</h2>    <p>6小节介绍了 <strong> ModularizationArchitecture </strong> 的基本使用, <strong> ModularizationArchitecture </strong> 提供了完整的demo,大家可以clone代码参考,有任何问题可以在该项目下提issue,欢迎交流。</p>    <p> </p>    <p> </p>    <p>来自:http://mp.weixin.qq.com/s?__biz=MzA3OTk4ODkwNA==&mid=2449245748&idx=1&sn=7413078e04770dff1bda56253fa24f31&chksm=8ba337f3bcd4bee5f40f494cd9c9228246097f10fc0a65a38b6e641878941f25981fca005169&scene=0#rd</p>    <p> </p>