Android技术|「Android」Binder机制入门学习笔记
Binder是什么?
- Binder是一种进程通信机制
- Binder是一个虚拟物理设备驱动
- Binder是一个能够发起通信的类
- 提升稳定性
每个进程互相独立,子进程崩溃不会影响主进程的稳定性(微信小程序、插件化插件…) - 突破内存限制
虚拟机对每个进程限制了内存大小,让某些组件运行在独立进程中,可以降低OOM的发生概率,也可以降低被系统杀死的概率(加载图片…) - 业务需要
某些通信或服务进程需要保活,插件化接入插件开发需要(消息推送进程…)
在学习Binder机制前,先简单说说Linux现有的所有进程间IPC机制:
- 管道:在创建时分配一个page大小的内存,缓存区大小比较有限
- 消息队列:信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信
- 共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决
- 套接字:作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信
- 信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段
- 信号:不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等
- 从性能的角度 数据拷贝次数:Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,但共享内存方式一次内存拷贝都不需要。从性能角度看,Binder性能仅次于共享内存。
- 从稳定性的角度Binder是基于C/S架构的,Client端有什么需求,直接发送给Server端去完成,架构清晰明朗,Server端与Client端相对独立,稳定性较好;而共享内存实现方式复杂,没有客户与服务端之别, 需要充分考虑到访问临界资源的并发同步问题,否则可能会出现死锁等问题。从稳定性角度看,Binder架构优于共享内存。
- 传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份,安全保护措施完全由上层协议来确保;Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志,前面提到C/S架构,Android系统中对外只暴露Client端,Client端将任务发送给Server端,Server端会根据权限控制策略,判断UID/PID是否满足访问权限。从安全性角度看,Binder架构优于传统IPC。
- Linux是基于C语言(面向过程),而Android是基于Java语言(面向对象),而对于Binder恰恰也符合面向对象的思想,将进程间通信转化为通过对某个Binder对象的引用调用该对象的方法,而其独特之处在于Binder对象是一个可以跨进程引用的对象,它的实体位于一个进程中,而它的引用却遍布于系统的各个进程之中。可以从一个进程传给其它进程,让大家都能访问同一Server,就像将一个对象或引用赋值给另一个引用一样。Binder模糊了进程边界,淡化了进程间通信过程,整个系统仿佛运行于同一个面向对象的程序之中。从语言层面的角度看,Binder更适合基于面向对象语言的Android系统,对于Linux系统可能会有点“水土不服”。
Binder | 共享内存 | Socket | |
---|---|---|---|
性能 | 拷贝一次 | 无需拷贝 | 拷贝两次 |
特点 | 基于C/S架构,易用性高 | 控制复杂,易用性差 | 基于C/S架构,通用接口,传输效率低、开销大 |
安全 | 每个App分配UID,同时支持实名和匿名 | 依赖上层协议,访问接入点是开放的 | 依赖上层协议,访问接入点是开放的 |
文章图片
【Android技术|「Android」Binder机制入门学习笔记】无论是注册服务和获取服务的过程都需要ServiceManager,需要注意的是此处的Service Manager是指Native层的ServiceManager(C++),并非指framework层的ServiceManager(Java)。
ServiceManager是整个Binder通信机制的大管家,是Android进程间通信机制Binder的守护进程。当Service Manager启动之后,Client端和Server端通信时都需要先获取Service Manager接口,才能开始通信服务。
图中Client/Server/ServiceManage之间的相互通信都是基于Binder机制。既然基于Binder机制通信,那么同样也是C/S架构,则图中的3大步骤都有相应的Client端与Server端。
- 注册服务(addService):Server进程要先注册Service到ServiceManager。该过程:Server是客户端,ServiceManager是服务端。
- 获取服务(getService):Client进程使用某个Service前,须先向ServiceManager中获取相应的Service。该过程:Client是客户端,ServiceManager是服务端。
- 使用服务:Client根据得到的Service信息建立与Service所在的Server进程通信的通路,然后就可以直接与Service交互。该过程:client是客户端,server是服务端。
图中的Client,Server,Service Manager之间交互都是虚线表示,是由于它们彼此之间不是直接交互的,而是都通过与Binder驱动进行交互的,从而实现IPC通信方式。其中Binder驱动位于内核空间,Client,Server,Service Manager位于用户空间。Binder驱动和Service Manager可以看做是Android平台的基础架构,而Client和Server是Android的应用层,开发人员只需自定义实现Client、Server端,借助Android的基本平台架构便可以直接进行IPC通信。
Binder的应用:AIDL AIDL = Android Interface Definition Language,主要目的是简化调用Binder的流程。
在Android中使用AIDL需要首先编写xxx.aidl文件,然后Android Studio会根据该.aidl文件自动生成xxx.java文件
- 手动编写:xxx.aidl
interface IDemoAidlInterface {void functionA(int arg);
void functionB(String arg);
}
- 自动生成:xxx.java
* This file is auto-generated.DO NOT MODIFY.
*/
package com.example.serverdemo;
public interface IDemoAidlInterface extends android.os.IInterface {public void functionA(int arg) throws android.os.RemoteException;
public void functionB(java.lang.String arg) throws android.os.RemoteException;
/**
* Default implementation for IDemoAidlInterface.
*/
public static class Default implements com.example.serverdemo.IDemoAidlInterface {@Override
public void functionA(int arg) throws android.os.RemoteException {}@Override
public void functionB(java.lang.String arg) throws android.os.RemoteException {}@Override
public android.os.IBinder asBinder() {return null;
}
}/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.serverdemo.IDemoAidlInterface {static final int TRANSACTION_functionA = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_functionB = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
private static final java.lang.String DESCRIPTOR = "com.example.serverdemo.IDemoAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {this.attachInterface(this, DESCRIPTOR);
}/**
* Cast an IBinder object into an com.example.serverdemo.IDemoAidlInterface interface,
* generating a proxy if needed.
*/
public static com.example.serverdemo.IDemoAidlInterface asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.serverdemo.IDemoAidlInterface))) {return ((com.example.serverdemo.IDemoAidlInterface) iin);
}
return new com.example.serverdemo.IDemoAidlInterface.Stub.Proxy(obj);
}public static boolean setDefaultImpl(com.example.serverdemo.IDemoAidlInterface impl) {// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Stub.Proxy.sDefaultImpl != null) {throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}public static com.example.serverdemo.IDemoAidlInterface getDefaultImpl() {return Stub.Proxy.sDefaultImpl;
}@Override
public android.os.IBinder asBinder() {return this;
}@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {java.lang.String descriptor = DESCRIPTOR;
switch (code) {case INTERFACE_TRANSACTION: {reply.writeString(descriptor);
return true;
}
case TRANSACTION_functionA: {data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
this.functionA(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_functionB: {data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
this.functionB(_arg0);
reply.writeNoException();
return true;
}
default: {return super.onTransact(code, data, reply, flags);
}
}
}private static class Proxy implements com.example.serverdemo.IDemoAidlInterface {public static com.example.serverdemo.IDemoAidlInterface sDefaultImpl;
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {mRemote = remote;
}@Override
public android.os.IBinder asBinder() {return mRemote;
}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;
}@Override
public void functionA(int arg) throws android.os.RemoteException {android.os.Parcel _data = https://www.it610.com/article/android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(arg);
boolean _status = mRemote.transact(Stub.TRANSACTION_functionA, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {getDefaultImpl().functionA(arg);
return;
}
_reply.readException();
} finally {_reply.recycle();
_data.recycle();
}
}@Override
public void functionB(java.lang.String arg) throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(arg);
boolean _status = mRemote.transact(Stub.TRANSACTION_functionB, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {getDefaultImpl().functionB(arg);
return;
}
_reply.readException();
} finally {_reply.recycle();
_data.recycle();
}
}
}
}
}
通过Binder进行进程通信的关键,在于自动生成的xxx.java文件,而非xxx.aidl文件,后者只是为了简化开发者编写生成前者的操作。
在文件编写生成完成后,在Android中使用AIDL进行远程调用的关键动作为以下三个:
- client端绑定server端服务
- client端获取server端IBinder对象
- client端远程调用server端接口
文章图片
client端获取server端IBinder对象
com.example.serverdemo.IDemoAidlInterface.Stub#asInterface
如果客户端与服务端处于同进程:
return ((com.example.serverdemo.IDemoAidlInterface) iin);
如果客户端与服务端处于异进程:
return new com.example.serverdemo.IDemoAidlInterface.Stub.Proxy(obj);
client端远程调用server端接口
文章图片
参考与推荐 http://gityuan.com/2015/10/31…
最后 由于水平有限,有错误的地方在所难免,未免误导他人,欢迎大佬指正!码字不易,感谢大家的点赞关注!有一起学习的小伙伴可以关注下我的公众号——【
??程序猿养成中心??
】每周会定期做关于Android的技术分享。推荐阅读
- android第三方框架(五)ButterKnife
- Android中的AES加密-下
- 八、「料理风云」
- 带有Hilt的Android上的依赖注入
- 「#1-颜龙武」区块链的价值是什么()
- 《深度倾听》第5天──「RIA学习力」便签输出第16期
- android|android studio中ndk的使用
- 「按键精灵安卓版」关于全分辨率脚本的一些理解(非游戏app)
- Android事件传递源码分析
- RxJava|RxJava 在Android项目中的使用(一)