Binder
名词:
IPC:进程间通信
UID:User ID,每一个程序仅有一个ID
PID:Process ID,进程ID
PPID:当前进程父进程ID
Server:提供服务:由于服务端的方法在服务端的Binder线程池中运行
Clint:获取服务
ServiceManager:帮助系统维护更多的Service列表
扩展
IPC:管道pipe,有名管道FIFO,MessageQueue,共享存储,Socket,Binder
https://blog.csdn.net/weixin_42197191/article/details/82867154?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
什么时候用到多进程(跨进程调用)
WebView,加载图片,推送,app调用外部服务(电话服务,闹钟服务)
为什么用多进程
app运行内存由系统分配给虚拟机,基本上都是32M,48M, 64M。
所以加载图片,网络放到别的进程,
其他进程崩溃不会影响到主进程
进程之间内存不共享,不能直接通信
android为什么用binder
基于linux底层实现的,只复制一次,
- Binder机制的核心是在客户端Client创建一个代理,在服务端Server创建一个存根,通过代理和存根之间的调用来完成进程间的数据交换
- Binder是Android系统中的一种IPC进程间通信结构。
- Binder的整个设计是C/S结构,客户端进程通过获取服务端进程的代理,并通过向这个代理接口方法中读写数据来完成进程间的数据通信。
4个方面的原因
1.安全:每个进程都会被Android系统分配UID(User ID 系统自动发放)和PID(进程ID),不像传统的在数据里加入UID,这就让那些恶意进程无法直接和其他进程通信,进程间通信的安全性得到提升。
2.高效:像Socket之类的IPC每次数据拷贝都需要2次,而Binder只要1次,在手机这种资源紧张的情况下很重要。
3.稳定性:Server端与Client端相对独立,稳定性较好。而共享内存实现方式复杂, 需要充分考虑到访问临界资源的并发同步问题,否则可能会出现死锁等问题
- 面试要回答进程间的通信,C/S架构,安全,UID/PID,代理接口方法,在serviceManager中注册了,只copy一次
binder通信概述:
1.从表面上来看,是client通过获得一个server的代理接口,对server进行直接调用;
2.实际上,代理接口中定义的方法与server中定义的方法是一一对应的;
3.client调用某个代理接口中的方法时,代理接口的方法会将client传递的参数打包成为Parcel对象;
4.代理接口将该Parcel发送给内核中的binder driver.
5.server会读取binder driver中的请求数据,如果是发送给自己的,解包Parcel对象,处理并将结果返回;
6.整个的调用过程是一个同步过程,在server处理的时候,client会block住。
Service Manager
任何service在被使用之前,均要向SM(Service Manager)注册,同时客户端需要访问某个service时,应该首先向SM查询是否存在该服务。如果SM存在这个service,那么会将该service的handle返回给client,handle是每个service的唯一标识符。
Messager.send 发送消息,服务端可以通过handler的handleMessage处理消息
send(msg)前通过msg.replyTo = mMessenger将自己的信使设置到消息中,这样服务端接收到消息时同时也得到了客户端的信使对象了,然后服务端可以通过
//得到客户端的信使对象,并向它发送消息
cMessenger = msg.replyTo;
cMessenger.send(message);
AIDL
- 基础
通过Binder通信需要按照binder的规矩去通信,代码实现起来比较复杂,AIDL已经帮我们实现了这个代码。实现进程间通信,尤其是在涉及多进程并发情况下的进程间通信。实现进程间通信,尤其是在涉及多进程并发情况下的进程间通信。
基本数据类型(short 除外)、String、charSequehce、List(元素得序列化)、Map(元素得序列化)、parcelable。
aidl方法传入自定义类对象,in、out、inout必须写(aidl默认支持的类型不用写,默认且只能是in),否则报错。
BroadcastReceiver 占用的系统资源比较多
Messenger 进行跨进程通信时请求队列是同步进行的,无法并发执行
2. 怎么实现
- 先建立一个AIDL接口(只写方法,不能声明静态成员),自动生成java接口文件
- 建立服务类,service子类,
- 实现由aidl生成的java接口
- 在AndroidManifest中配置AIDL服务,标签中android:name的属性值就是客户端要引用该服务的ID,也就是Intent类的参数值。
概念:
远程服务:运行在其他应用里面的服务
本地服务:运行在自己应用里面的服务
进程间通信 IPC
AIDL即 本地访问远程服务
客户端和服务器都需要有相同的aidl文件,
在gradle文件中配置,识别出java包以外的java文件
有其他自定义的属性要在建立aidl并把相关的java文件放在aidl下
自定义的属性客户端要实现Parcelable接口
客户端开启服务端服务,
private void attemptToBindService() {
Intent intent = new Intent();
intent.setAction("com.vvvv.aidl");
intent.setPackage("com.iiiv.aidlserver");
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
客户端使用ServiceConnection 接口 ,实现onServiceConnection和onServiceDisconnection方法,具体如下
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(getLocalClassName(), "service connected");
//IBinder对象转化为aidl接口对象,IBinder就是服务器端onBinder时回传的Binder
//通过.Stub.asInterface(传来的IBander)
messageCenter = MessageCenter.Stub.asInterface(service);
mBound = true;
if (messageCenter != null) {
try {
//调用aidl接口的方法,给
mInfoList = messageCenter.getInfo();
Log.e(getLocalClassName(), mInfoList.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
服务器端:
实现aidl接口.Stub,重写方法
private final MessageCenter.Stub messageCenter = new MessageCenter.Stub(){
@Override
public List<Info> getInfo() throws RemoteException {
synchronized (this){
if(messages != null ){
return messages;
}
return new ArrayList<>();
}
}
@Override
public void addInfo(Info info) throws RemoteException {
synchronized (this){
if(messages == null){
messages = new ArrayList<>();
}
if(info == null){
info = new Info();
}
if(!messages.contains(info)){
messages.add(info);
}
Log.e("wuzhen", "客户传来了数据" + messages.toString());
}
}
};
//这里返回值给客户端,返回值是上面的名字
public IBinder onBind(Intent intent) {
return messageCenter;
}
aidl文件
//所有的返回值前都不需要加任何东西,不管是什么数据类型
//传参时除了Java基本类型以及String,CharSequence之外的类型
//都需要在前面加上定向tag,具体加什么量需而定
AIDL 的编写主要为以下三部分:
- 创建 AIDL
创建要操作的实体类,实现 Parcelable 接口,以便序列化/反序列化
新建 aidl 文件夹,在其中创建接口 aidl 文件以及实体类的映射 aidl 文件
Make project ,生成 Binder 的 Java 文件 - 服务端
创建 Service,在其中创建上面生成的 Binder 对象实例,实现接口定义的方法
在 onBind() 中返回 - 客户端
实现 ServiceConnection 接口,在其中拿到 AIDL 类
bindService()
调用 AIDL 类中定义好的操作请求
• Android提供了一个 AIDL (Android接口定义语言)工具来处理序列化和通信。这种情况下Service需要以aidl文件的方式提供服务接口,AIDL工具将生成一个相应的java接口,并且在生成的服务接口中包含一个功能调用的stub服务桩类。Service的实现类需要去继承这个 stub服务桩类。Service的onBind方法会返回实现类的对象,之后就可以使用它来进行通信了。
1.创建AIDL文件,接口方法
2.对象序列化
3.服务端写好,直接复制到客户端,AIDL文件,对象文件复制过来,包名不能变
①Client–》②bindservice–》③service返回IBinder–》④Client拿到IBinder对象可以像同进程一样调用
方法流程
④Client拿到IBinder对象可以像同进程一样调用:AIDL自动生成java文件,能够拿到,
aidl.java文件:
数据:Client --> Service用Proxy 发送 进站口
Service --> Client 用Stub 接受 出站口
客户端调用方法–》mRemote.transact()–》Binder
Binder–》onTransact–》this.相同的方法–》实现类去具体实现方法
binderServer怎么返回的IBinder
mbase.bindService 也就是 ContexImpl.bindService–bindServiceCommon
MS–AMS