Binder框架及运用详解

来源:互联网 发布:战舰世界mac 国服切换 编辑:程序博客网 时间:2024/05/25 01:35
     这部分来了解学习Binder的内容,摘自柯元旦的《Android内核剖析》,放在博客里主要是学习起来方便。
    Binder的词义是别针、回形针。我们经常用别针把两张纸“别”在一起,而在Android中,Binder用于完成进程间通信(IPC),即把多个进程"别"在一起。比如,普通应用程序可以调用音乐播放器提供的播放、暂停、停止等功能。
    Binder工作在Linux内核层,属于一个驱动,只是这个驱动不需要硬件,或者说其操作的硬件是基于一小段内存。
Binder框架
Binder是一种架构,它提供了服务端接口、Binder驱动、客户端接口三个模块。
1. 首先来看服务端
   一个Binder服务端实际上就是一个Binder类的对象,该对象一旦创建,内部就启动一个隐藏线程。该线程接下来会接收Binder驱动发送的消息,收到消息后,会执行到Binder对象中的onTransact()函数,并按照该函数的参数执行不同的服务代码。因此,要实现一个Binder服务,就必须重载onTransact()方法。
    可以想象,重载onTransact()函数的主要内容是把onTransact()函数的参数转换为服务函数的参数,而onTransact()函数的参数来源是客户端调用transact()函数时传递进来的,因此,如果transact()有固定格式的输入,那么onTransact()就会有固定格式的输出。
2. 下面再看Binder驱动
     任意一个服务端Binder对象被创建时,同时会在Binder驱动中创建一个mRemote对象,该对象的类型也是Binder类。客户端要访问远程服务时,都是通过mRemote对象。
3. 最后来看应用程序客户端
   客户端想要访问远程服务,必须获取远程服务在Binder服务中对应的mRemote引用,至于如何获取,后面再介绍。获得该mRemote对象后,就可以调用其transact()方法,而在Binder驱动中,mRemote对象也重载了transact()方法,重载的内容主要包括以下几项:
1> 以线程间消息通信的模式,向服务端发送客户端传递过来的参数
2> 挂起当前线程,当前线程正是客户端线程,并等待服务端线程执行完指定服务函数后通知(notify)
3> 接收到服务端线程的通知,然后继续执行客户端线程,并返回到客户端代码区
 从这里可以看出,对应用程序开发人员来讲,客户端似乎是直接连接远程服务对应的Binder,而事实上则是通过Binder驱动进行了中转,即存在两个Binder对象,一个是服务端的Binder对象,另一个则是Binder驱动中的Binder对象,所不同的是Binder驱动中的对象不会再额外产生一个线程。

设计Service端
 设计Service端很简单,从代码的角度来讲,只要基于Binder类新建一个Service类即可。
示例:假设该Service仅两个方法:start(String filePath)和stop(),那么该类的代码可以如下:
public class MusicPlayerService extends Binder{    @override    protected boolean onTransact(int code,Parcel data,Parcel reply,int flags) throws RemoteException{        return super.onTransact(code,data,reply,flags);    }    public void start(String filePath){    }    public void stop(){    }}          

当要启动该服务时,只需要初始化一个MusicPlayerService对象即可。比如可以在主Activity里面初始化一个MusicPlayerService,然后运行,此时可以在ddms中发现多了一个线程。

                 

  如果不创建MusicPlayerService,则只有三个Binder对象对应的线程,这三个线程的详情参照第6章。
定义了服务类后,接下来需要重载onTransact()方法,并从data变量中读出客户端传递的参数,比如start()方法所需要的filePath变量。然而,这里有个问题,服务端如何知道这个参数在data变量中的位置呢?
因此,这就需要调用者和服务者双方有个约定。这里假定客户端在传入的包裹data中放入的第一个数据就是filePath变量,那么onTransact()的代码可以这样写:
switch(code){    case 1000:        data.enforceInterface("MusicPlayService");        String filePath=data.readString();        start(filePath);        //reply.writexxx();   //返回一些值        break;}           
code变量用于标识客户端期望调用服务端哪个函数,因此,双方需要约定一组int值,不同的值代表不同的服务端函数,该值和客户端的transact()函数中第一个参数code的值是一致的。
enforeInterface()是为了某种校验,它与客户端的writeInterfaceToken()对应。
readString()用于从包裹中取出一个字符串。取出filePath变量后,就可以调用服务端的start()函数了。
如果该IPC调用的客户端期望返回一些结果,则可以在返回包裹reply中调用Parcel提供的相关函数写入相应的结果。


Binder客户端设计 
 要想使用服务端,首先要获取服务端在Binder驱动中对应的mRemote变量的引用。获得该变量的引用后,就可以调用该变量的transact()方法,该方法的函数原型如下:
 public final boolean transact(int code,Parcel data,Parcel reply,int flags)
 其中data表示的是要传递给远程Binder服务的包裹(Parcel),远程服务函数所需要的参数必须放入这个Parcel中。包裹中只能放入类型的变量,这些类型包括常用的原子类型,比如String,int,long等,要查看包裹可以放入的全部数据类型,可以参照Parcel类。除了一般的原子变量外,Parcel还提供了一个writeParcel()方法,可以在包裹中包含一个小包裹。因此,要进行Binder远程服务调用时,服务函数的参数要么是一个原子类,要么必须继承于Parcel类(查看这篇文章:Intent传递自定义对象),否则是不能传递的。
  对于MusicPlyerService的客户端而言,可以这样调用transact()方法:
IBinder mRemote=null;String filePate="/sdcard/music/heal_the_world.mp3";int code=1000;Parcel data=Parcle.obtain();Parcel reply=Parcel.obtain();data.writeInterfaceToken("MusicPlayerService");data.writeString(filePath);mRemote.transact(code,data,reply,0);      //调用Binder的transact()方法IBinder binder=reply.readStrongBinder();reply.recycle();data.recycle();          
   首先,包裹不是客户端自己创建的,而是调用Parcel.obtain()申请的,这正如生活中的邮局一样,邮票是来自于邮局的。其中data和reply变量都由客户端提供,reply变量用户服务端把返回的结果放入其中。
  writeInterfaceToken()方法标注远程服务名称,该名称将作为Binder驱动确保客户端的确想调用指定的服务端。
  writeString()方法用于向包裹中添加一个String变量。注意:包裹中添加的内容是有序的,这个顺序必须是客户端和服务端事先约定好的,在服务端的onTransact()方法中会按照约定的顺序取出变量。
    接着调用transact()方法,调用该方法后,客户端线程进入Binder驱动,Binder驱动就会挂起当前线程,并向远程服务发送一个消息,消息中包含了客户端传进来的包裹。服务端在拿到包裹后,会对包裹进行拆解,然后执行指定的服务函数,执行完毕后,再把执行结果放入客户端提供的reply包裹中。然后服务端向Binder驱动发送一个notify的消息,从而使得客户端线程从Binder驱动代码返回到客户端代码区。
    transact()的最后一个参数flags的含义是执行IPC调用的模式,分为两种:一种是双向,用0表示,其含义是服务端执行完指定服务后会返回一定的数据;另一种是单身,用1表示,表示不返回任何数据。
最后,客户端就可以从reply中解析返回的数据了,同样,返回包裹中包含的数据也必须是有序的,而这个顺序也必须是双方事先约定好的。

  • 具体程序编写流程及方法,可以查看这篇文章:《老罗Android第二季》使用Service(2)及Binder、AIDL
0 0
原创粉丝点击