学习了一段时间android了,看了好多技术文章,最近突发奇想也想写一点东西,可不知道从何写起,所以随便写一点吧,如果日后有什么好的整体思路的时候再费时间去整理吧,谁让我是个懒人呢。废话不多,今天就小试牛刀,写一些关于Binder的学习笔记吧。(声明一下,所讲的仅限在应用水平上,如果各位想深究内核的话可以看一下老罗的博客,本文参考资料是《android内核剖析》)
binder,英文的意思是“粘合剂”,那它把什么东西粘合在一起呢?其实Binder是一种架构,包含了客户端、Binder驱动、服务端三个模块。那有人问了为什么不直接客户端和服务端,非得加个桥梁呢?也就是Binder 到底有什么用。Binder提供了一种“全局服务”,保证了系统中各个应用都可以访问到。直接上图,以它为线索:
首先看服务端,服务端就是一个Binder类对象,该对象一旦创立就会开启一个隐藏线程,来接受Binder驱动发送来的消息然后执行onTransact()函数,所以服务端必须重载onTransact()方法,onTransact()方法里面的参数正是经过包装过的客户端传过来的参数,那么客户端通过哪个方法包装传递参数呢?(请接着看。。)
下面再看Binder驱动,服务端Binder对象一旦建立,对应的驱动里面就是建立一个mRomote对象,mRemote也是BInder类的,客户端访问服务端服务都是通过这个mRemote。
最后看一下客户端。客户端访问服务端服务都是通过mRemote引用调用transact()方法(解决了服务端留下的问题),驱动中说到mRemote对象是在驱动里面创建的,那怎么会跑到客户端里面的呢?我认为这个问题是打通整个Binder机制最重要的一环,下面几节都将围绕这个问题展开,即客户端如何拿到mRemote引用?
接下来趁热打铁,说一下服务端和客户端的设计实现(都是基于《android内核剖析》的例子讲解)!!!
服务端实现:前面讲到服务端必须重载onTransact()方法。
public class MusicPlayService 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 path){
}
public void stop(){
}
}
MusicPlayService初始化以后ddms就会多了一个binder线程,即存在两个binder对象,一个是服务端binder对象,另一个是binder驱动里的binder对象。依次看一下onTransact()方法中的参数。
1)int code 用于标识客户端期望调用服务端的哪个函数
2)Parcel data ,Parcel reply 分别是客户端传给服务端的参数的包装和服务端返回给客户端的数据包装。
3)int flags 标识该条信息传递是单向还是希望服务端返回数据的(0标识双向,1标识单向)
客户端实现:
IBinder mRemote = null;
String path = "/sdcard/music/heal_the_world.mp3";
int code = 2000; // 与服务端约定好的标识
Parcel data = Parcel.obtain(); //通过Parcel.obtain();好像是从邮局里买信封一样,我们并不自己制作信封
Parcel reply = Parcel.obtain();
data.writeInterfaceToken("MusicPlayService"); //客户端标识我要调用的服务,既然已经拿到对应服务的mRemote引用了所以这句作用不大
data.writeString(path);
mRemote.transact(code,data,reply,0); //开始打包
IBinder binder = reply.readStrongBinder();
reply.recycle();
data.recycle();
我们还是暂时不去想客户端是如果拿到mRemote引用的呢,拿到mRemote引用后客户端就可以用transact()函数了。
到这里我们已经清楚了Binder机制的一个大体实现,不过还是有几个问题需要解决的,接下来我们就结合android中具体的实例来分析Binder实现IPC的过程!