【内核研究】Binder客户端设计

来源:互联网 发布:proteus单片机教程 编辑:程序博客网 时间:2024/05/29 09:30

要想使用服务端,首先要获取服务端在Binder驱动中对应的mRemote变量的引用。获得该变量的引用后,就可以调用该变量的transact()方法。该方法的函数原型如下:

public final boolean transact(int code, Parcel data, Parcel reply,int flags) 

其中data表示的是要传递给远程Binder服务的包裹(Parcel),远程服务函数所需要的参数必须放入这个包裹中。包裹中只能放入特定类型的变量,这些类型包括常用的原子类型,比如String、int、long等,要查看包裹可以放入的全部数据类型,可以参照Parcel类。除了一般的原子变量外,Parcel还提供了一个writeParcel()方法,可以在包裹中包含一个小包裹。因此,要进行Binder远程服务调用时,服务函数的参数要么是一个原子类,要么必须继承于Parcel类,否则,是不能传递的。

因此,对于MusicPlayerService的客户端而言,可以如下调用transact()方法。

        IBinder mRemote = null;        String filePath = "/sdcard/music/heal_the_world.mp3";        int code = 1000;        Parcel data = Parcel.obtain();        Parcel reply = Parcel.obtain();        data.writeInterfaceToken("MusicPlayerService");        data.writeString(filePath);        mRemote.transact(code, data, reply, 0);        IBinder binder = reply.readStrongBinder();        reply.recycle();        data.recycle();

现在来分析以上代码。首先,包裹不是客户端自己创建的,而是调用Parcel.obtain()申请的,这正如生活中的邮局一样,用户一般只能用邮局提供的信封(尤其是EMS)。其中data和reply变量都由客户端提供,reply变量用户服务端把返回的结果放入其中。

writeInterfaceToken()方法标注远程服务名称,理论上讲,这个名称不是必需的,因为客户端既然已经获取指定远程服务的Binder引用,那么就不会调用到其他远程服务。该名称将作为Binder驱动确保客户端的确想调用指定的服务端。

writeString()方法用于向包裹中添加一个String变量。注意,包裹中添加的内容是有序的,这个顺序必须是客户端和服务端事先约定好的,在服务端的onTransact()方法中会按照约定的顺序取出变量。

接着调用transact()方法。调用该方法后,客户端线程进入Binder驱动,Binder驱动就会挂起当前线程,并向远程服务发送一个消息,消息中包含了客户端传进来的包裹。服务端拿到包裹后,会对包裹进行拆解,然后执行指定的服务函数,执行完毕后,再把执行结果放入客户端提供的reply包裹中。然后服务端向Binder驱动发送一个notify的消息,从而使得客户端线程从Binder驱动代码区返回到客户端代码区。

transact()的最后一个参数的含义是执行IPC调用的模式,分为两种:一种是双向,用常量0表示,其含义是服务端执行完指定服务后会返回一定的数据;另一种是单向,用常量1表示,其含义是不返回任何数据。

最后,客户端就可以从reply中解析返回的数据了,同样,返回包裹中包含的数据也必须是有序的,而且这个顺序也必须是服务端和客户端事先约定好的。

0 0
原创粉丝点击