瞻仰了前辈们的研究成果, 并掺入了自己的理解, 如有不对, 敬请批评.
为什么Android要使用Binder
Binder 作为一种 IPC 机制, 在 Linux 内有很多的前辈, 为什么 google 会创建这么一种新的方式呢?
Linux 现有 IPC 方式有这几类:
- 管道:在创建时分配一个page大小的内存,缓存区大小比较有限;
- 消息队列:信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;
- 共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;
- 套接字:作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信;
- 信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
- 信号: 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;
与以上方式相比较, Binder 有以下特点:
- 性能较好: 数据拷贝 Binder 只需要一次, 管道/消息队列/socket 都需要两次, Binder 仅次于共享内存(一次都不需要);
- 稳定性较好: 基于C/S架构的Binder在逻辑上更加解耦, 架构清晰, 而共享内存容易出现各种并发同步死锁问题;
- 安全性好: Android每个App都有自己的UID, 传统的 IPC 机制的接收方无法拿到发送方可靠的UID, 而 Binder 的 Server 端可以通过 Android 给每个 App 暴露出的 Client 端获取到, 从而对不同UID的App进行权限判断等安全性控制;
- 语言层面: Linux基于C, Android基于java, Binder机制更符合面向对象的环境;
- 协议: Linux 受开源代码许可协议GPL的保护, 如果上层应用调用到 Linux Kernel, 就必须也遵循GPL协议; Android利用Binder隔离了Linux Kernel层, 把GPL控制在内核空间, 而用户空间采用了允许不反馈源码的Apache-2.0协议, 并在中间采用BSD授权, 有利于Google实现开源和商业化的共存
综上所述, Binder是Android系统中IPC的最好方式.
Binder 概述
Binder是android内独有的跨进程通信方式, 它在native层有一套完整的C/S架构, framework层也通过jni技术实现了一套镜像功能的Binder C/S架构, framework层的binder功能最终都交给native的binder来完成. 引用一张Gityuan大神的Binder架构图:
- 对于Android Driver层: Binder可以理解为一种虚拟的物理设备, 它的设备驱动是/dev/binder, 连接了Video,Camera等设备;
- 对于Android Native层: Binder是创建Service Manager/BpBinder/BBinder模型、搭建与binder驱动的桥梁;
- 对于Android Framework层: Binder是各种Manager(ActivityManager, WindowManager等)和对于ManagerService的桥梁;
- 对于Android App层: Binder是客户端和服务端通信的桥梁, 服务端包括统一进程下的和需要使用AIDL的不同进程下的服务. bindService的时候, 服务端会返回一个包含服务端方法及数据的Binder对象, 通过这个Binder对象, 客户端就能与之通信了.
Binder如何工作
Binder的数据传输 就是发送端把binder_transaction节点,插入到目标进程或其子线程的todo队列中,等目标进程或线程不断循环地从todo队列中取出数据并进行相应的操作.
在Binder驱动层,每个接收端进程都有一个todo队列,用于保存发送端进程发送过来的binder请求. 线程在空闲时进入可中断的休眠状态,当自己的todo队列或所属进程的todo队列有新的请求到来时便会唤醒,执行新的请求事务, 执行完毕后再次休眠.
再引入一张图, 并添加了注释
App层的两种服务里的binder使用
App层的Service有同进程下的Service和不同进程的Service两种, 新开进程的Service需要在AndroidManifest.xml里的Service声明中添加 android:process=":remote"
, 也可以添加服务名称.
同进程下的Service
Client 端
public class FragSocketPresenter extends BasePresenter<IFragSocketView> {
private MyService.ServiceBinder normalBinder
private ServiceConnection normalConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
normalBinder = (MyService.ServiceBinder) service;
normalBinder.setSocketStateListener(normalListener);
}
@Override
// 此方法只有在service异常时才调用
public void onServiceDisconnected(ComponentName name) {
normalBinder = null;
}
};
public void startConnect() {
if (getView() != null) {
Intent intent = new Intent(getView().getContext(), MyService.class);
intent.putExtra("IP", getView().getIp());
getView().getContext().bindService(intent, normalConnection, Context.BIND_AUTO_CREATE);
}
}
}
服务端
public class MyService extends Service {
private String ip = "";
@Override
public IBinder onBind(Intent intent) {
ip = intent.getStringExtra("IP");
return new ServiceBinder();
}
public class ServiceBinder extends Binder {
public void setSocketStateListener(SocketStateListener normalListener) {
...
}
}
}
基于AIDL的跨进程服务
篇幅有限, 写到另一篇里了 AndroidStudio下使用 AIDL 构建跨进程 Service( 详细代码贴图 ), 填补网上的大多数坑
参考: 源码大神 Gityuan 的各类文章
http://gityuan.com/2015/10/31/binder-prepare/
https://www.zhihu.com/question/39440766/answer/89210950?from=profile_answer_card
来源:CSDN
作者:叽哩叽哩鸡
链接:https://blog.csdn.net/j550341130/article/details/80593518