在android中Binder用于完成进程间通信,Binder工作在Linux层,属于一个驱动,只是这个驱动不需要硬件,其操作的硬件基于某一小端内存。从线程的角度来讲,Binder驱动代码运行在内核态,客户端程序调用Binder是通过系统调用完成的。
Binder是一种架构,这种架构提供了服务端接口、Binder驱动、客户端接口三个模块。
![](https://i-blog.csdnimg.cn/blog_migrate/1329b38d3969550d7074fc6178d471e5.png)
服务端:一个服务端实际上就是一个Binder类的对象,这个对象一旦创建,就会启动一个隐藏线程。该线程用来接收Binder驱动发送的消息,接收到消息后,会执行到Binder对象中的onTransact()方法。因此Binder服务的实现要重载onTransact()方法。
Binder驱动:任意一个服务端Binder对象创建时,同时会再Binder驱动中创建一个mRemote对象,该对象的类型也是Binder类。客户端要访问远程服务,都通过mRemote对象进行访问。
客户端:客户端要想访问远程服务,必须获取远程服务的Binder对象中的对应的mRemote引用,通过调用mRemote的transact()方法,访问远程服务。
在Binder驱动中,mRemote对象也重载了transact()方法,重载的主要内容如下:
- 以线程通信的方式,向服务端发送客户端传递过来的参数
- 挂起当前线程(客户端线程),并等待服务器端线程执行完指定任务后的通知
- 接收服务端线程通知,然后执行客户端线程,并返回到客户端代码区
从上面可以看出,客户端程序似乎只直接调用了远程服务对应的Binder,而实际上则是通过Binder 驱动进行了中转。实际上是存在两个Binder对象,一个是服务端的Binder对象,一个是驱动中的Binder对象,驱动中的Binder对象不会额外产生一个线程,直接就是客户端程序的线程。
关于Binder的设计
设计Service端:只需要实现Binder类,然后重写Binder的protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) ;
然后根据code执行部同的操作,从data中读取客户端传过来的数据,这里需要注意一下data传过来参数的顺序,得和客户端有个约定才行。
执行完操作之后,把reply存上数据,返回给客户端
设计客户端:首先是获取服务端在Binder驱动中对应的mRemote变量的引用,就可以调用mRemote的transact()方法了。
对于以上的设计,需要解决的两个问题是:
- 客户端如何获得服务端的Binder对象引用
- 客户端和服务端如何约定参数在Parcel中的传递顺序和服务端如何知道调用什么服务的函数。
对于第一个问题而言,如果可以方便应用程序开发者的话,系统应该把这个功能给提供出来,android系统确实是这么做的,通过
Service可以提供Binder的引用。第二个问题程序员既可以自行实现,也可以使用aidl工具进行默认实现
获得服务端的Binder对象引用
在Activity中可以使用startService()启动一个服务,这个实现在android.app.ContextImpl类中实现的,
在这里的时候只是把服务开启了,还是没有服务端的Binder的引用的。
public boolean bindService(Intent service, ServiceConnection conn,
int flags) 这个方法才能真正的返回服务端的binder引用。
不过需要实现ServiceConnection 接口,接口定义如下:
public interface ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service);
public void onServiceDisconnected(ComponentName name);
}
当Service能正常启动,然后系统会为远程的服务Binder的引用,onServiceConnected(ComponentName name, IBinder service)里的
service就是那个服务端的Binder引用。
客户端和服务端的参数传递
关于客户端和服务端的参数传递,androidSDK中提供了一个aidl的工具,这个工具可以把aidl文件转化为java文件,并且重载了transact()
和onTransact()方法,统一了存入包裹和读取包裹的参数,程序员只需关注业务代码实现即可。
aidl的具体使用其实很简单,就是把一个借口文件的修饰符删除,把文件名修改成以.aidl结尾的文件即可,如下::
![](https://i-blog.csdnimg.cn/blog_migrate/b6e9e36c432dee2537bf8d53a0b75d84.png)
关于具体的规则,自行去官方文档查看。
下面主要分析aidl工具生成的java类
![](https://i-blog.csdnimg.cn/blog_migrate/08f829ccf7b28008cf974134d5d8fafa.png)
![](https://i-blog.csdnimg.cn/blog_migrate/26d40321c3d6899b250032f088a15f7d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/cba4551020ca07bb66ffc557906aba10.png)
![](https://i-blog.csdnimg.cn/blog_migrate/da80adf443e19b74046628f59701dfce.png)
![](https://i-blog.csdnimg.cn/blog_migrate/a7def0b5eff009cc70784fce86cad61c.png)
第7行 定义了一个IMusicService接口,并继承了android.os.IInterface接口,这个接口有一个方法定义asBinder(),这个asBinder接口就
是用来返回Binder对象的。
第9行 定义了一个抽象内部类Stub,这个内部类继承了android.os.Binder并实现了com.suchangli.binderdemo.IMusicService。
这里重点解释一下,为什么要设计成抽象类,这个抽象类主要是给服务端用的,服务端继承这个Stub类,去实现具体的业务代码。
并且重载了transact()和onTransact()方法,包裹数据的存入顺序已经由aidl定义好。
第59行 定义了一个Proxy类,该类将作为客户端访问服务端的代理。
第94行 TRANSACTION_start常量,这个常量就是onTransact()中的第一个参数,用来标示执行什么服务动作的。
第22-32行 这个是实现的asInterface()方法,这个方法的作用是返回一个Binder对象,当然,这里返回的是 com.suchangli.binderdemo.IMusicService对象。看看里面的实现很有意思,调用这个方法的客户端,如果是本地的客户端,
不许要跨进程调用,那么直接在进程内部调用就可以了,只有事远程的客户端才会返回一个Binder代理。这个asInterface()提供了一个
通用的访问接口,不管是本地调用还是远程的调用,都会返回一个Binder对象,这个对象有可能就是直接实现的Stub实例或者是Proxy的实例。