实现Android跨进程组件通信能有多简单?

实现Android跨进程组件通信能有多简单?

作为一个Android开发,都要会点组件化知识。组件化的主要的特点,是剥离依赖,但组件间不直接依赖后,通信问题要怎么解决呢。

通常我们用的一下这种类似Binder通信的C/S架构,由一个ServiceManager服务管理器作为一个桥梁提供服务注册和服务查询,业务上要进行通信就是以下三部曲:定义服务接口,发布服务,使用服务。
组件通信三部曲

这套架构在单一进程间运行很简单且直观,我们需要做的就是把服务接口下沉到公共依赖,在组件A实例化服务接口的对象,然后通过ServiceManager进行服务发布,组件B通过ServiceManager获取并使用服务就好了。但要想跨进程,就有点麻烦了。

保持这套架构的场景下,我们可以去写AIDL,让ServiceManager支持管理Binder类型的服务就好了,ServiceManager的角色用一个Android的Service去实现,每个组件要发布、查询服务,直接通过bind这个ServiceManager就可以了。通信模型大致如下:

跨进程组件通信模型

看来做跨进程通信支持,模型变化也并不大,还剩下的问题就仅仅是每个服务类型的实现了,通常来讲就是定义AIDL以及实现Binder服务类。市面上很多框架也确实做到了这点,比如爱奇艺开源的Andromeda就支持跨进程的服务发布。

但我们还是得自己写AIDL,远程服务的使用端用IInterface代理的远程服务对象,每次使用都要处理RemoteException也要处理。实际上写过AIDL的朋友们可能知道这体验并不好。我觉得麻烦的点主要在于:

  1. 定义AIDL接口文件(无IDE自动提示支持)
  2. 编译AIDL生成Stub类 (需预先编译一次生成)
  3. 继承Stub类并实现AIDL中定义的接口 (必须继承Stub类型,不如普通服务的实现类只需实现对应的接口,可以继承其他类进行代码复用)
  4. 通过代理接口进行远程调用必须捕获RemoteException (要么每个调用都catch处理一下,要么写个包装类wrapper把异常吃掉返回默认值)

如果涉及到接口的改动,又要编译两次,改AIDL文件,改Stub类实现文件,如果在第4步用了包装类,还得改包装类文件……

所以,跨进程通信的问题点主要麻烦在于AIDL书写起来太麻烦了!

现在的期望是:通过现在流行的APT(注解处理工具)技术动态生成这些模板代码,让我们回到最初,还是只关注接口、实现就好了,让跨进程组件通信和普通组件通信一样简单、优雅。

那要怎么做呢,继续往下看!

如何简化AIDL实现?

仔细看来,AIDL流程中真正的实现核心还在于接口定义(对应AIDL定义)和接口实现(Stub实现),与普通服务定义并无二致。唯一比进程间服务多的处理大概就是远程服务需要考虑的RemoteException的处理了。整体实现过程其实好多都是模板化的工作,真正的核心也就是普通的接口定义以及实现,只要我们能够自动生成那些模板类,把实际实现代理给接口的实现类就好了。基于这种情况,我们来考虑使用动态生成代码的方式来避免写这些模板代码。

哪些东西可以自动生成
  1. AIDL文件:AIDL与Interface接口功能是一致的,我们可以根据普通的Interface定义生成AIDL
  2. Stub类的实现:可自动生成Stub实现,内部只需要代理Interface的实现即可把远程调用转发给Interface的实现类
  3. Binder接口代理:如上面分析,ADIL通信中远端拿到的是服务的代理接口,每个调用都需要捕获RemoteException,我们可以自动生成这样的一个代理,将异常通过统一的方式转发出来,对于不想做特殊处理的调用者,提供默认的实现捕获异常或者重新抛出RuntimeException类型的异常,反正就是不需要调用者必须在每个方法的调用时进行异常处理了,让调用者对于是否要处理调用的异常有选择的权利。
新的跨进程通信模型

假设我们已经完成以上文件的自动生成,若定义一个IMusic的接口,提供音乐相关服务,我们会自动生成如下文件:

  1. IMusicBinder.aidl: AIDL定义文件,编译后会生成IMusicBinderIMusicBinder.Stub
  2. IMusicRemote.java:继承IMusicBinder.Stub,用于发布到远端,内部代理IMusic的具体实现。
  3. IMusicRemoteProxy.java:实现IMusic接口,用于远端服务调用,代理IMusicBinder远程服务,内部处理每个调用的异常。

另外假设MusicServiceIMusic的实现类,那新的模型大致就可以做成下面的样子:
SimpleService跨进程通信模型

流程大致描述为:

  1. 进程A的服务实现组件中,通过SimpleService把实现了IMusic的MusicService对象作为远程服务发布。
  2. 进程A的SImpleService内部把MusicService包装成IMusicRemote发布到RemoteServiceManager。RemoteServiceManager可以实现为上面讲到的一个Android的Service,用于真正的管理所有的远程服务对象的Binder。
  3. 进程B的服务使用组件中通过SimpleService发起IMusic服务的获取请求。
  4. 进程B的SimpleService向RemoteServiceManager请求IMusicRemote对象。
  5. RemoteServiceManager返回保存的IMusicRemote对象给进程B的SimpleService。
  6. 进程B的SimpleService将IMusicRemote对象包装成IMuisicRemoteProxy返回给服务使用组件。

自此,服务使用组件就拿到了一个IMusic服务对象,可以进行跨进程通信了。

于是,我们完成了简化的进程间通信模型,解答了小节标题提出的问题。
这样我们就可以只定义普通的接口和接口实现,但却等发布为一个跨进程的服务了!

我们可以:不用再写AIDL,不用再等两遍编译,不用再考虑每次调用跨进程方法需要处理烦人的RemoteException,不用再害怕修改一点接口而去改很多个文件了,甚至可以用上IDE的重构功能,轻松修改接口定义实现……

具体实现,开始使用

经过几周的努力,目前大概实现了一个可用的版本: github传送

具体怎么用呢,看下面:

一、先看看普通组件服务发布使用流程,如下:
// 1. 公共依赖模块,声明服务接口
interface IMusic {
   
  fun play(name: String)
}

// 2. 音乐组件模块,实现服务接口
class MusicService
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值