Binder 框架(二)aidl的使用

来源:互联网 发布:国内财经新闻 知乎 编辑:程序博客网 时间:2024/05/10 18:34

Android SDK 提供了一个aidl工具,可以把aidl文件自动转换成一个对应的java类文件,该Java类文件重载了onTransact()方法,统一了存入(transact()方法中)包裹(Parcel)和 读出包裹(onTransact()方法中)的参数。


示例:编写一个IMusicPlayerService服务,服务中包含两个服务函数,分别是start() 和 stop(), 先来写一个IMusicPlayerService.aidl文件 :

package com.soft,android,client;interface IMusicPlayerService(){boolean start(String filePath);void stop();}

此时aidl工具会在 gen 目录下产生一个IMusicPlayerService.java文件,内容如下:(程序中此文件由aidl自动产生,为了说明特加了注释)

/* * This file is auto-generated.  DO NOT MODIFY. * Original file: /Users/zhangyijun-egame/Desktop/MultiProcessTest/MultiProcessTest/src/com/myq/android/MultiProcessTest/IMusicPlayerService.aidl */package com.myq.android.MultiProcessTest;public interface IMusicPlayerService extends android.os.IInterface {/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implementscom.myq.android.MultiProcessTest.IMusicPlayerService {private static final java.lang.String DESCRIPTOR = "com.myq.android.MultiProcessTest.IMusicPlayerService";/** Construct the stub at attach it to the interface. */public Stub() {this.attachInterface(this, DESCRIPTOR);}/** * Cast an IBinder object into an * com.myq.android.MultiProcessTest.IMusicPlayerService interface, * generating a proxy if needed. 无论是通过IPC还是本进程调用通过此方法返回一个统一的接口对象 * 如果是IPC调用则直接通过代理的方式进行访问,此时调用接口的方法是 Proxy类中实现的方法(start(),stop()) * 如果是本进程调用则直接返回接口对象 ,此时调用接口的方法是实现了 Stub类中实现的方法(start(),stop()) *  */public static com.myq.android.MultiProcessTest.IMusicPlayerService asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof com.myq.android.MultiProcessTest.IMusicPlayerService))) {return ((com.myq.android.MultiProcessTest.IMusicPlayerService) iin);}return new com.myq.android.MultiProcessTest.IMusicPlayerService.Stub.Proxy(obj);}/** * IInterface接口的API原文:Base class for Binder interfaces. When defining a * new interface, you must derive it from IInterface. * 简单翻译下:BInder接口的基类,当要定义一个新接口时,必须继承IInterface *  * 此方法的API原文:Retrieve the Binder object associated with this interface. * You must use this instead of a plain cast, so that proxy objects can * return the correct result. * 简单翻译下:要关联此接口重新得到Binder对象,就必须使用此方法而不是简单的转换,以便于proxy对象能返回正确的结果。 * 继承IInterface 必须实现的方法 */@Overridepublic android.os.IBinder asBinder() {return this;}/** * 服务端接收到驱动发来信息时调用此方法,并按照不同的code参数执行相应的服务 (只发生在IPC时) 进程内调用不走此过程 */@Overridepublic boolean onTransact(int code, android.os.Parcel data,android.os.Parcel reply, int flags)throws android.os.RemoteException {switch (code) {case INTERFACE_TRANSACTION: {reply.writeString(DESCRIPTOR);return true;}case TRANSACTION_start: {data.enforceInterface(DESCRIPTOR);java.lang.String _arg0;_arg0 = data.readString();boolean _result = this.start(_arg0);reply.writeNoException();reply.writeInt(((_result) ? (1) : (0)));return true;}case TRANSACTION_stop: {data.enforceInterface(DESCRIPTOR);this.stop();reply.writeNoException();return true;}}return super.onTransact(code, data, reply, flags);}/** * 代理类 *  */private static class Proxy implementscom.myq.android.MultiProcessTest.IMusicPlayerService {private android.os.IBinder mRemote;/** * 获得服务端binder的引用 *  * @param remote */Proxy(android.os.IBinder remote) {mRemote = remote;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}/** * 实现了接口中的方法发生IPC时调用,统一了包裹内参数,以方便在服务端onTransact()中读取 */@Overridepublic boolean start(java.lang.String filePath)throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();boolean _result;try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeString(filePath);// 向服务端线程发消息,此时挂起客户端线程,并等待服务端通知完成再返回客户端代码区mRemote.transact(Stub.TRANSACTION_start, _data, _reply, 0);_reply.readException();_result = (0 != _reply.readInt());} finally {_reply.recycle();_data.recycle();}return _result;}/** * 实现了接口中的方法发生IPC时调用,统一了包裹内参数,以方便在服务端onTransact()中读取 */@Overridepublic void stop() throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);// 向服务端线程发消息,此时挂起客户端线程,并等待服务端通知完成再返回客户端代码区mRemote.transact(Stub.TRANSACTION_stop, _data, _reply, 0);_reply.readException();} finally {_reply.recycle();_data.recycle();}}}/** * 作为code参数用来标识要执行的服务 */static final int TRANSACTION_start = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_stop = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);}/** * 在继承Service类中创建 Stub类的对象时实现此方法,用于进程内调用 */public boolean start(java.lang.String filePath)throws android.os.RemoteException;/** * 在继承Service类中创建 Stub类的对象时实现此方法,用于进程内调用 */public void stop() throws android.os.RemoteException;}


个人认为看懂这个java文件并了解执行过程后binder的java层调用就80%了。

这个类干了三件事:

1)、定义了一个Java interface,包含aidl文件所声明的服务函数,类名和aidl相同并且该类基于IInterface接口,所以需要提供一个asBinder()函数。

2)、定义了一个Proxy类,该类将作为客户端程序访问服务端的代理,

3)、定义了一个Stub类,是一个abstract类,基于Binder类,并且实现IMusicPlayerService接口,主要由服务端来用,服务函数的具体功能要有程序员实现所以此类为       abstract类型。




理解此图的关键是asInterface()函数:

asInterface()函数通过queryLocalInterface()方法判断是IPC调用还是进程内调用,结构都返回一个服务接口对象。

左边:首先不是IPC 通信 所以binderService()返回的直接是服务端的binder对象引用,所以就可以直接调用接口的服务函数。

右边:首先是IPC通信,所以binderService()返回的是驱动中的binder对象引用,使用Proxy类处理数据。


看这个事有一个问题困扰了好久,现在想来是看糊涂了当时:

就是不知道Binder怎么把客户端中赋予的参数带过的,其实得到服务端binder对象引用后,在ServiceConnection 接口的ServiceConnected()方法中就得到了一个服务接口的对像,通过此对象调用服务接口的方法时就要传参数了,此时如果是IPC方式则 调用 Proxy类中的服务方法并传值,这样参数经统一包裹输入后传给服务端,服务端拿到参数后在调用实现了Stub的类中的如服务函数,如果此时不是IPC 也就是进程内通信那就直接调用实现了Stub的类中的服务函数。


以上是根据自己的理解所写,有什么不对请大家指出,共同讨论。

附上实验Demo :服务端  客户端


本文出自 “linuxcjh” 博客,请保留此出处http://blog.csdn.net/linuxcjh/article/details/8688407


原创粉丝点击