android开发艺术IPC之Binder

1.Binder优点

Android是基于Linux内核基础上设计的,却没有把管道/消息队列/共享内存/信号量/Socket等一些IPC通信手段作为Android的主要IPC方式,而是新增了Binder机制,其优点有:

  • 传输效率高、可操作性强:传输效率主要影响因素是内存拷贝的次数,拷贝次数越少,传输速率越高。几种数据传输方式比较:从Android进程架构角度分析:对于消息队列、Socket和管道来说,数据先从发送方的缓存区拷贝到内核开辟的缓存区中,再从内核缓存区拷贝到接收方的缓存区,一共两次拷贝;而对于Binder来说,数据从发送方的缓存区拷贝到内核的缓存区,而接收方的缓存区与内核的缓存区是映射到同一块物理地址的,节省了一次数据拷贝的过程
  • 实现C/S架构方便:Linux的IPC方式除了Socket以外都不是基于C/S架构,而Socket主要用于网络间的通信且传输效率较低。Binder基于C/S 架构 ,Server端与Client端相对独立,稳定性较好。
  • 安全性高:传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份;而Binder机制为每个进程分配了UID/PID且在Binder通信时会根据UID/PID进行有效性检测。

2.Binder框架

Binder框架定义了四个角色:Server,Client,ServiceManager和Binder驱动。
在这里插入图片描述

  • ServiceManager:服务的管理者,将Binder名字转换为Client中对该Binder的引用,使得Client可以通过Binder名字获得Server中Binder实体的引用
    在这里插入图片描述
  • Binder驱动:
    与硬件设备没有关系,其工作方式与设备驱动程序是一样的,工作于内核态。
    提供open()、mmap()、poll()、ioctl()等标准文件操作。
    以字符驱动设备中的misc设备注册在设备目录/dev下,用户通过/dev/binder访问该它。
    负责进程之间binder通信的建立,传递,计数管理以及数据的传递交互等底层支持。
    驱动和应用程序之间定义了一套接口协议,主要功能由ioctl()接口实现,由于ioctl()灵活、方便且能够一次调用实现先写后读以满足同步交互,因此不必分别调用write()和read()接口。
    其代码位于linux目录的drivers/misc/binder.c中。
  • Server&Client:服务器&客户端。在Binder驱动和Service Manager提供的基础设施上,进行Client-Server之间的通信。

3.Binder 工作原理:

服务器端:在服务端创建好了一个Binder对象后,内部就会开启一个线程用于接收Binder驱动发送的消息,收到消息后会执行onTranscat(),并按照参数执行不同的服务端代码。
Binder驱动:在服务端成功Binder对象后,Binder驱动也会创建一个mRemote对象(也是Binder类),客户端可借助它调用transcat()即可向服务端发送消息。
客户端:客户端要想访问Binder的远程服务,就必须获取远程服务的Binder对象在Binder驱动层对应的mRemote引用。当获取到mRemote对象的引用后,就可以调用相应Binder对象的暴露给客户端的方法。

从API角度:是一个类,实现IBinder接口。
从IPC角度:是Android中的一种跨进程通信方式。
从Framework角度:是ServiceManager连接各种Manager和相应ManagerService的桥梁。
从应用层:是客户端和服务端进行通信的媒介。客户端通过它可获取服务端提供的服务或者数据。

Android开发中, Binder主要用在Service中,包括AIDL和Messenger,其中普通Service中的Binder不涉及进程间通信,而Messenger的底层其实是AIDL,所以选择用AIDL来分析Binder的工作机制。为了分析Binder的工作机制,新建一个AIDL示例,SDK会自动为我们生产AIDL所对应的Binder类,然后分析Binder的工作过程。
例如:
新建三个文件分别是Bookjava:表示图书信息的类,实现了Parcelable 接口

  • Book aidl: Book 类在AIDL中的声明
  • IBookManager.aidl是接口里面实现Book数据的自定义方法
  • 系统会自动生成Binder类:
    首先声明IbookManager.aidl中声明的方法;然后通过整型id标记这两个方法;最后声明内部类Stub (属于一个Binder类),当客户端和服务端都位于同一个进程时,方法调用不会走跨进程的transact过程;而当两者位于不同进程时,方法调用需要走transact 过程,这个逻辑由Stub的内部代理类Proxy来完成。这个接口的核心实现就是它的内部类Stub和Stub的内部代理类Proxy

解析

  • DESCRIPTOR
    Binder的唯一标识,一般用当前Binder 的类名表示
  • asInterface(android.os.IBinder obj)
    用于将服务端的Binder 对象转换成客户端所需的AIDL接口类型的对象,这种转换过程是区分进程的,如果客户端和服务端位于同一进程, 那么此方法返回的就是服务端的Stub对象本身,否则返回的是系统封装后的Stub.proxy对象。
  • asBinder
    此方法用于返回当前Binder对象。
  • onTransact
    这个方法运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。该方法的原型为public Boolean onTransact (int code, android.os.Parcel data, android.os Parcel reply, int flags)。服务端通过code可以确定客户端所请求的目标方法是什么,接着从data中取出目标方法所需的参数(如果目标方法有参数的话),然后执行目标方法。当目标方法执行完毕后,就向reply中写入返回值(如果目标方法有返回值的话)。需要注意的是,如果此方法返回false, 那么客户端的请求会失败

客户端发起远程请求时,当前线程会被挂起直至服务器端进程返回数据,所以不能再UI线程中请求耗时的远程方法;
服务器端的Binder方法运行在Binder的线程池中,所以Binder方法不管是否耗时都应该采用同步的方式去实现,因为他已经在一个线程中了。

4.手动生成Binder

(1)声明一个AIDL性质的接口,只需要继承Inteface接口即可,Interface接口中只有一个asBinder方法。在接口中声明Binder描述符和另外两个id,这两个id分别表示的是getBookList和addBook方法
(2)实现Stub类和Stub类中的Proxy代理类。Binder 运行在服务端进程,如果服务端进程由于某种原因异常终止,这个时候我们到服务端的Binder连接断裂(称之为Binder 死亡),会导致我们的远程调用失败。如果我们不知道Binder连接已经断裂,那么客户端的功能就会受到影响。为了解决这个问题,Binder中提供了两个配对的方法linkToDeath和unlinkToDeath,通过linkToDeath可以给Binder设置一个死亡代理, 当Binder死亡时,我们就会收到通知,这个时候我们就可以重新发起连接请求从而恢复连接。
步骤:
首先,声明一个DeathRecipient对象。DeathRecipient 是一个接口,其内部只有一个方法binderDied,我们需要实现这个方法,当Binder 死亡的时候,系统就会回调binderDied方法,然后我们就可以移出之前绑定的binder代理并重新绑定远程服务:

private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient()
	@Override
	public void binderDied() {
		if (mBookManager == null)
			return;
		}
		mBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0) ;
		mBookManager = null ;
		// TODO:这里重新绑定远程Service
	}	
};

其次,在客户端绑定远程服务成功后,给binder设置死亡代理:

mService = IMess ageBoxManager.Stub.asInterface(binder) ;
binder.linkToDeath(mDeathRecipient, 0);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值