这是关于USB Mass Storage的架构图。
上图关系为:
StorageManager为Client,MountService是Server,通过AIDL进行进程间通信。
MountService是一个Android Service,由systemserver启动。
Volume Daemon(Vold)是一个Native Service,有Init.c读取init.rc后启动。
MountService和Vold之间通过Socket通信。
NativeDaemonConnector帮助MountService取得Vold的socket,建立通信。
Vold通过NetLink读取Kernel的uevent.
NetLinkManager帮助Vold建立与kernel间的通信
注:对于上图,我准备分以下几个方面阐述:
1. 《
AIDL之Jave Framework层实现》
2. 《
AIDL之 Native Framework层实现》
3. 《
AIDL之Kernel层实现:Binder机制》
4. 《
Vold 通信详解》
5. 《
Vold Framework层分析》
IPC
IPC是一系列面向多线程间数据交换的技术集合。这里所说的多线程间的数据交换,包括进程内线程间的数据交换和进程间的线程数据交换。如果进程运行在不同的PC上,需要网络建立连接完成进程间通信。(这种情况极其复杂)。IPC技术的方法被分为:
消息发送、
同步、
内存共享和
远程过程调用(RPC)这几方面功能。(
WIKI)个人理解
IPC:进程间通信,就是在不同进程之间传播或交换信息。进程的用户空间是互相独立的,一般而言是不能互相访问的,唯一的例外是
共享内存区。但是,系统空间却是“公共场所”,所以
内核显然可以提供这样的条件。除此以外,那就是双方都可以访问的外设了。在这个意义上,两个进程当然也可以通过磁盘上的
普通文件交换信息,或者通过“注册表”或其它数据库中的某些表项和记录交换信息。广义上这也是进程间通信的手段,但是一般都不把这算作“进程间通信”。(
百度百科)
两种解释,WIKI侧重定义;百度百科侧重应用。进程的实体是线程,进程间的通信说到根本就是线程间的通信。这包括以下几个应用场合
l 同一计算机,进程内的多线程通信。(同步、互斥、锁等技术和概念)这点我们一般不做IPC,仅仅按多线程技术处理。
l 同一计算机,进程间的通信。
l 不同计算机,进程间的通信。
RPC:远程过程调用。在计算机科学领域,RPC被认为是IPC的一种。RPC可以在PC内或者通过网络在PC间进行function call。程序员可以将一些核心的API集中到一个程序中,通过RPC,其他需要该核心API的程序可以直接调用,提高代码的共享程度。(
WIKI)
RPC是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。
RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了
传输层和应用层。RPC使得开发包括网络分布式多程序在内的
应用程序更加容易。RPC采用客户机/服务器
模式。 请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保 持睡眠状态直到调用信息的到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用过程接收答复信息,获得进程结果,然后调用执行继续进行。(百度百科)
Android中的Service大概有如下三种(据我所知):
Android Application Service:在Android AP开发中常常用到的一种概念。从最直白的视角来看,就是剥离了界面的Activity,它们在很多Android的概念方面比较接近,都是封装有一个完整的功能逻辑实现,只不过Service不抛头露脸,只是默默无声的做坚实的后盾。概括说,这一组件是利用Framework中的AIDL,采用拿来主义实现RPC其更详尽的原理请参看
《深入Android 【三】-组件入门》。
Native Service:在Native Framework这一层中提供支持的一种服务。系统启动阶段解析Init.rc过程中启动Native Service(包括各种用户空间的Daemon),即在Init第一阶段启动。
Android Service:在Java Framework这一层为系统提供支持的一种服务,又称Java Service。由SystemServer启动。即在Init第二阶段启动。
本节中关注是Native service和Android Service。下面介绍他们的启动过程
在system/core/init中init.c读取init.rc等系列配置文件:
启动Native Service:
启动Zygote,Zygote启动sytemserver建立Android service:
SystemServer启动Android Service:
Android
所谓
Android 中Service架构指的是Android Service、Native Service是如何联系起来,以及如何与底层进行通信。
在
android中,各种××××Manager为AP提供支持,管理系统的运行。××××Manager充当一个Client,××××ManagerService充当Server为××××Manager提供支持。简单,经典的C/S架构。这里的各种××××ManagerService 就是指Android Service,都在Java Framework空间,且都在systemserver进程中。Native Service通过socket或者直接JNI,Android Service提供支持。Native Service在Native Framework(C/C++空间)中。以上有所元素全部在android用户空间中。
至于
Native Service与Kernel的通信主要有Function call、poll、select、NetLink等几种
采用Service架构方式是比较标准的做法,即图上蓝色线的部份;红色线的部份为非Service架构式的做法。Service在Android框架里的角色是「存取底层硬件」,往上层的话,可以和应用程序沟通。因此,采用标准的Service做法,好处是在数据存取(data communication)的处理较为系统化。这方面,Android提供了标准的处理架构。图上的「core libraries」即是Service程序代码的实现,也就是,Android应用程序透过JNI(Dalvik)来到Service这一层,再透过Service加载*.so文件;而非标准做法则是让应用程序直接透过JNI来加载*.so文件。
红色的过程,因为不使用Service架构,因此「框架整合」的工作量比较小,甚致大部份的实现都不需要改动框架本身。这样的做法,就有点像是「跳过framework」的方式;相对的,此时应用程序开发者需要考虑的设计议题就比较多。
未来的Android发展趋势,应会以第二种做法为主,即Manager API直接与Native Service沟通,以达到更好的效能表现。
Android AIDL
通常每个应用程序都在它自己的进程内运行,但有时需要在进程间传递对象,你可以通过应用程序UI的方式写个运行在一个不同的进程中的service。在Android平台中,一个进程通常不能访问其他进程中的内存区域。所以,他们需要把对象拆分成操作系统能理解的简单形式,以便伪装成对象跨越边界访问。编写这种伪装代码相当的枯燥乏味,好在我们提供了AIDL工具可以来做这件事。
AIDL(Android接口描述语言)是一个IDL语言,它可以生成一段代码,使在一个Android设备上运行的两个进程使用内部通信进程进行交互。如果你需要在一个进程中(例如:在一个Activity中)访问另一个进程中(例如:一个Service)某个对象的方法,你就可以使用AIDL来生成这样的代码来伪装传递各种参数。
AIDL IPC的机制是基于接口的,和COM或Corba类似,但它是轻量级的。它使用代理类在客户端和实现层间传递值。
AIDL
Android USB Mass Storage
打个比方,你到自动取款机上去取款;你就是客户,取款机就是你的代理;你不会在乎钱具体放在那里,你只想看到足够或更多的钱从出口出来(这就是com的透明性)。你同银行之间的操作完全是取款机代理实现。你的取款请求通过取款机,传到另一头,银行的服务器,他也没有必要知道你在哪儿取钱,他所关心的是你的身份,和你取款多少。当他确认你的权限,就进行相应的操作,返回操作结果给取款机,取款机根据服务器返回结果,从保险柜里取出相应数量的钱给你。你取出卡后,操作完成。取款机不是直接同服务器连接的,他们之间还有一个“存根”,取款机与存根通信,服务器与存根通信。从某种意义上说存根就是服务器的代理。(
AIDLFramework层的架构,如下图:
换而言之,Android就是在传统的C/S架构中加入了一层,实现IPC。图中表明,AIDL类似COM的Proxy/Stub架构。不过是现在android自己的序列化类Pacel。
以USB Mass Storage架构举例。看看到底什么是AIDL的framework层的实现。编写好aidl文件,运用aidl工具生成IMountService.java文件,如下所示(删除部分内容,方便举例)
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: frameworks/base/core/java/android/os/storage/IMountService.aidl
*/
package android.os.storage;
/** WARNING! Update IMountService.h and IMountService.cpp if you change this file.
* In particular, the ordering of the methods below must match the
* _TRANSACTION enum in IMountService.cpp
* @hide - Applications should use android.os.storage.StorageManager to access
* storage functions.
*/
public interface IMountService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements android.os.storage.IMountService
{
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
。。。。。。。。。。。
case TRANSACTION_registerListener:
{
data.enforceInterface(DESCRIPTOR);
android.os.storage.IMountServiceListener _arg0;
_arg0 = android.os.storage.IMountServiceListener.Stub.asInterface(data.readStrongBinder());
this.registerListener(_arg0);
reply.writeNoException();
return true;
}
。。。。。。。。。。。
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements android.os.storage.IMountService
{
。。。。。。。。。。。。。。。。
/**
* Registers an IMountServiceListener for receiving async
* notifications.
*/
public void registerListener(android.os.storage.IMountServiceListener listener) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_registerListener, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
。。。。。。。。。。。。。。。。
}
可以看到IMountService 中的Stub继承Binder和IMountService
看过源代码,我们不难发现上图中对应的角色:
Client ------ StorageManager
Proxy ------ IMountServie.Stub.Proxy
Parcel ------对象序列化类,数据只有继承Parcelable才能进行RPC
Stub ------ IMountService.Stub
Server ------ MountService
当StorageManager调用MountService方法时,例如调用
registerListener,步骤如下:
? 进入IMountServie.Stub.Proxy找到对应的方法
registerListener。
? IMountServie.Stub.ProxyregisterListener利用Parcel将函数调用的序列化为android理解的结构
? 调用onTransact函数,onTransact根据参数,找到对应registerListener Switch-case语句执行
? 数据通过Binder机制进行写操作,客户端调用阻塞,等待服务端reply
? 服务端处理完request返回
? 客户端取回数据
AIDL
使用AIDL实现一个IPC有下列步骤:
l 创建你的AIDL文件-这个文件定义一个接口(YourInterface.aidl),该接口定义了可供客户端访问的方法和属性。
l 添加AIDL文件到你的makefile中-(Eclipse plugin可以帮你管理)。Android包括编译器,AIDL调用,这些都能在tools/directory中找到。
l 实现接口方法-AIDL编译器从你的AIDL接口中使用JAVA编程语言来创建一个接口。这个接口有一个名为Stub的内部抽象类,它继承接口(并实现供IPC调用的所必需的几个附加方法)。你必须创建一个类来实现该接口。
l 向客户端开放接口-如果你写个service,你应该扩展该Service并重载getBinder()方法来返回一个实现上述接口的类的实例。
Step1创建一个AIDL文件
AIDL语法简单,你可以用来声明一个带一个或多个方法的接口,也可以传递参数和返回值。这些参数和返回值可以是任何类型,甚至是其他的AIDL生成的接口。然而,值得重视的是你必须导入所有的non-bult-in类型,即使他们已经作为接口在其他包里定义了。下面是些AIDL支持的数据类型:
简单Java编程语言类型(int,boolean等) -不需要import声明。
下面类之一(不需要import声明)
l String
l List - List中的所有元素都必须是可支持的类型中的一个,包括其他AIDL生成接口和parcelables。List可以作为泛型类来灵活使用(比如List<String>)。而实际的接受方的类则总是ArrayList,尽管该方法将被生成来使用List接口。
l Map - Map中的所有元素都必须是可支持的类型中的一个,包括其他AIDL生成接口和parcelables。泛型化的Maps(比如:Map<Stirng,Integer>)不被支持。而实际的接受方的类则总是HashMap,尽管该方法将被生成去使用Map接口。
l CharSequence - CharSequence的作用是可以被TextView和其他Widget对象使用。
其他的AIDL生成接口通过引用方式进行传递。所以import声明是必须的。封装协议实现的自定义的类是值传递的方式。所以import声明也是必须的。
下面是基本的AIDL语法:
实现接口,扩展YourInterface.Stub,并实现方法成员。(你可以创建一个aidl文件并实现stub方法而不用绑定-Android创建过程在java文件之前会处理aidl文件)。
这里有个例子,它实现了一个调用IRemoteService的接口,并使用匿名实例公开一个简单的方法gerPid():
实现接口时有几个原则:
l 抛出的异常不要返回给调用者。
l an "Application is Not Responding" dialog). Try to call them in a separate thread.
l IPC调用是同步的。如果你知道一个IPC服务需要超过几毫秒的时间才能完成地话,你应该避免在Activity/View线程中调用。
l 因为它会挂起应用程序(Android可能会显示"应用程序没有响应"对话框)。试着在一个独立的线程中调用。
Step3向客户端公开接口
现在你已完成了接口的实现,你需要向客户端公开该实现。这就是我们所熟悉的"发布服务"。发布一个Service,然后继承Service并实现getBinder()返回一个实现的类的实例。下面是个Service的代码片断,该Service向客户端公了IRemoteService接口。
Step4使用parcelables进行参数的值传递
注意:如果你现在使用Eclipse插件,Parcelables并不能工作。你会看到以下的错误信息:
l 仅声明parcelables的.aidl文件不需要写进makefile
l aidl只能生成接口代码,而不是parcelables。
这是个众所周知的局限。Parcelables仍然可以被ant build的xml文件或自定义的build系统所使用。你应该在Eclipse项目中添加一个工作区,该工作区可以为所有的接口手动运行aidl工具。下面的步骤5说明为何Eclipse不该尝试编译这些aidl文件。
这是个众所周知的局限。Parcelables仍然可以被ant build的xml文件或自定义的build系统所使用。你应该在Eclipse项目中添加一个工作区,该工作区可以为所有的接口手动运行aidl工具。下面的步骤5说明为何Eclipse不该尝试编译这些aidl文件。
如果你有类需要通过AIDL接口从一个进程发送到另一个,你必须确保类代码可以被IPC接收端所使用。通常这意味着一开始你就要和service进行通讯。
让类支持parcelable协议,有五点需要注意
l 让类实现Parcelable接口。
l 实现public void writeToParcel(Parcel out),该方法可以将当前对象的状态写入parcel.
l 实现public void readFromParcel(Parcel in),该方法可以在parcel中读出值到对象中.
l 向类中添加一个静态成员,名为CREATOR。该对象实现了Parcelable.Creator接口.
l 向parcelable类中添加一个.aidl文件,以便AIDl工具可以找到。但不要向build中添加该文件。该文件的用法类似于C中的头文件.你不需要为parcelable编译aidl文件,就像你不会编译个.h文件一样。
AIDL将使用代码中生成的这些方法和成员来伪装或解读对象。
下面的例子说明了Rect类如何实现了Parcelable协议.
Rect类中的伪装是相当简单的。仔细看看Parcel中的其他方法,你会看到其他各种值你都可以写进Parcel.
注意:不要忽视从其他进程接收数据时的安全性考虑。在本例中,rect将从parcel中读四个数字,而你的工作则是确保这些都在可接受的值得范围内而不管调用者想要干什么。Android中的安全和访问许可中有更多关于如何确保应用程序安全的信息。
注意:不要忽视从其他进程接收数据时的安全性考虑。在本例中,rect将从parcel中读四个数字,而你的工作则是确保这些都在可接受的值得范围内而不管调用者想要干什么。Android中的安全和访问许可中有更多关于如何确保应用程序安全的信息。
调用一个IPC方法
本系列中应经阐述了××××Manager和××××ManagerService之间的C/S关系。他们之间的电泳也已经说明。其实在Android中遍布拿来主义,Android四大组件Service IPC也是采用的AIDL。下面以一个Activity启动一个Service为例,讲解。
l 调用类调用远程接口的步骤:
l 声明一个接口类型的变量,该接口类型在.aidl文件中定义。
l 实现ServiceConnection。
l 调用ApplicationContext.bindService(),并在ServiceConnection实现中进行传递.
l 在ServiceConnection.onServiceConnected()实现中,你会接收一个IBinder实例(被调用的Service).调用YourInterfaceName.Stub.asInterface((IBinder)service)将参数转换为YourInterface类型。
l 调用接口中定义的方法。你总会捕捉到DeadObjectException异常,该异常在连接断开时被抛出。它只会被远程方法抛出。
l 断开连接,调用接口实例中的ApplicationContext.unbindService()
调用IPC服务需要注意几点:
l 对象在进程间被引用计数
l 匿名对象可以通过方法参数发送。