您的位置:首页 > 移动开发 > Android开发

Service与Android系统实现(1)-- 应用程序里的Service (五)

2013-09-06 11:47 260 查看

RPC,以及为什么需要这么复杂的处理

我们可以先来了解一下RPC的实现。

跨进程访问,实际上并非Android环境才需要,这是所有跨进程软件设计里的必须项。这种交互性的跨进程需求,跟我们传统的C/S(客户端/服务器)构架类似,客户端使用IPC访问服务,而服务器端则实现具体的代码逻辑,通过IPC提供服务。如下所示



进程1提供客户端功能,而进程2提供服务器功能,在进程1里调用RPCFunc(1,2),实际上会触发到进程2里的RPCFunc1Impl1(1,2)的执行。

需要通过IPC机制在底层把这样的访问实现出来,这样在客户端进程空间里可以找到RPCFunc1Stub()的定义,用于将函数调用解析为基于IPC的请求消息
在RPCFunc1Stub()实现里,会将访问请求转义成具体的进程间通信的消息,再发送出去
服务器端则会RPCFunc1Skel()来监听所有的请求,然后再具体调用请求转发到自己实现的RPCFunc1Impl()
当RPCFunc1Impl()执行完成之后,反将结果返回到RPCFuncSkel()
返回的值则会再次经由进程间通信,再传回给客户端
然后在客户端RPCFunc1()的return语言里返回
这样,从进程1的代码上来看,好像是完成了一次从RPCFunc1()到RPCFunc1Impl()的远程过程调用(RPC),但在内容实现上,则是经历了1-6这样6个步骤的串行操作。之说以说是串行,因为这6个步骤会顺序进行,进程1的执行RPCFunc1()这一函数时,直到第6步执行完成之前,都会被阻塞住。

通过通过串行实现后的这种特殊C/S框架,因为跟我们传递的函数调用类似,只是提供了跨进程的函数调用,于是根据这样的行为特征,我们一般会叫它们为远程过程调用(Remote Procedure Call,简称RPC)。支撑起RPC环境的是IPC通信机制,我们也知道套接字(Socket)也是IPC机制一种,而TCP/IP是Socket机制的一部分,于是很自然的,RPC也天生具备跨网络的能力,在实际的部署里,RPC一般会是网络透明的,通过RPC来进行访问的那端,并不会知道具体实现RPC的会是本地资源或是网络上的资源。RPC是实现复杂功能的基础,特别是一些分布式系统设计里,比如我们Linux环境里的网络文件系统NFS、AFS等,都是基于RPC,还有更高级一点,像我们的Corba环境、J2EE环境和WebService,都会使用RPC的变种。

拥有了RPC通信能力之后,我们在编程上的局限性便大为减小,我们的代码可以很灵活地通过多进程的方式进行更安全的重构,而且还可以进行伸缩度非常良好的部署。这点对于Android来说,尤为重要,因为我们的Android系统就是构建在基于多进程的“沙盒”模型之上的。在Android环境里,不存在基于网络来进行RPC的需求,而是使用高性能的面向对象式的Binder,于是,我们的RPC,需要通过RPC来构建。于是,简单的RPC通信流程,在Android系统里,则可以通过IBinder对象引用来完成,得到如下的实现逻辑:



我们会通过IBinder,来实现从一个进程来访问另一个进程的对象,得到远程对象的引用之后,虽然在进程1里我们像是真正通过这一IBinder来访问远程对象的某些方法,比如doXXX()方法,但实际上后续的执行逻辑则会被转到Binder IPC来发送访问进程,进程1在进入doXXX()方法之后,就会进入IBinder是否有返回的检测循环。当然此时由于IBinder设计上的精巧性,此时进程实际上会休眠到/dev/binder设备的休眠队列里。而提供RPC的进程2则相反,它启动后会一直在IPC请求上进行循环监听,当有IPC请求过来之后,则会将doXXX()的访问请求解析出来,访问这一方法,在访问完成之后,再将调用doXXX()的结果通过IBinder返回给发出调用请求的进程1。这时,会唤醒进程1继续往下执行,从IPC上取回调用的返回值,然后再执行doXXX()之后的代码。

通过这种RPC机制,在Android系统里,就可以更灵活地来设计交互过程,更方便地在多进程环境里进行低耦合化设计。在RPC交互的Server端的实现,可以灵活地根据自己的实现或是调用上的需求,开放出来一部分的自己实现的接口,从而给多个Client端提供服务。

由于Binder能够支持RPC,则基于代码有可能会变得异常复杂,于是,在实际的编程过程里,我们也还需要其他的辅助手段。比如,在实际的实现里,我们都会存在大量的RPC访问:



在这种大量的RPC实现里,会有大量地处理RPC调用的重复代码,比如RPC的发送部分,Server端实现的IPC解析与分发部分。这些重复代码是没有意义的,而且在实际过程里,这种重复代码也将会是错误的源头。想像一下,如果上图所描述的RPC有100个,此时,我们将需要实现一个多大的switch()跳转。还有一个设计上的问题,当然我们使用固化的switch()来处理这种大量分支跳转,则我们的代码在设计上会被固化,我们不能灵活地重构我们的代码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: