Android 四大组件:activity显示,service后台服务,broadcast通信, contentProvider 数据共享.
相关系列文章:
Activity总结和补充:https://www.jianshu.com/p/bd31881c28fc
Android Binder&Aidl理解和补充:https://www.jianshu.com/p/eb791ae04e2f
Service史上最全面解析》理解和补充: https://www.jianshu.com/p/444af9739135
Handler总结和补充:https://w独立进程ww.jianshu.com/p/a041c41af27d
目录
前言
下面这篇是读过的最好的关于binder和aidl的blog。
Android:远程服务Service(含AIDL & IPC讲解) https://www.jianshu.com/p/34326751b2c6。
图文详解 Android Binder跨进程通信的原理:https://www.jianshu.com/p/4ee3fd07da14.
重点是aidl的使用blog,binder仅仅为了更好的理解aidl.
下文是对上面blog的一些补充.
1 什么是binder
1.1 定义和作用
Binder是一套实现进程通信的系统,它自己通过一层一层的调用实现,参见文末附录。binder是client-server结构,给应用层client,server两个进程提供了注册,发送和接收的接口.
binder系统内部通过优化的共享内存机制,实现了发送和接收,对应用层透明。实际应用中,通常使用基于binder实现的aidl进行进程通信,所以binder接口通常对用户也是透明.
1.2 Binder跨进程原理
对于进程之间来说,用户空间的数据不可共享,内核空间的数据可共享.所以跨进程通信原理,是在内核空间进行数据交换.
内核内存是所有进程共享的,不区分进程,所以不存在某个进程自己的内核内存.
内核内存由内核页表管理,内核页表保存在内核的mm_struct中.所有进程的内核页表是一样的,进程页表是不一样的.
2 Binder 模块
Binder采用cs结构.
角色 | 作用 | 备注 |
---|---|---|
Service manager | 独立进程.主要功能有两个一个是提供服务注册功能(addservice),一个是提供服务检索功能(getservice) | 类似dns. |
binder | 通过它就可以在两个进程之间路由转发数据了 | 类似dns之上的路由器.server的service向service manager 注册获得binder, 所以binder属于server |
Server&Client |
3 使用binder实现通信
这节代码是为了将binder机制将清楚,实际应用层通常并不直接使用binder接口函数.而是使用基于binder实现的aidl,即第5节的内容。
binder原理参见https://www.jianshu.com/p/4ee3fd07da14,本节是binder的实现.
上面blog的代码分析有些复杂,需要先看下面的例子之后,再看上面blog的代码分析.
实现binder一共需要2步.
3.1 server 创建binder&在onTransact中实现service 函数
public class GameService extends Service {
private Binder mBinder = new Binder() {
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
private Binder mBinder = new Binder() {
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
if (code ==1 ) {
String _arg0;
_arg0 = data.readString();
int _result = getGamePrice(_arg0);
reply.writeInt(_result);
}
......//other case
}
public int getGamePrice(String name) {
int price = -1;
if ("逃生2".equals(name)) {
price = 88;
} else if ("饥荒".equals(name)) {
price = 24;
}
return price;
}
};
在Androidmanifest.xml 中添加service,Service manager管理Androidmanifest.xml 中的service。
3.2 client bindService & 调用server service 函数
- bindService: 客户端想和服务端通信,通过Service Manager查找到服务端的Binder,通过callback函数返回.
String action = "android.intent.action.bind.gameservice";
Intent intent = new Intent(action);
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
private IBinder mRemote = null;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mRemote = service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mRemote = null;
}
};
- client 调用server 函数,或者说通过transact向server发送数据
客户端通过调用transact()方法,将要请求的内容发送到服务段,通过code(1)区分不同server 函数。
private int getPrice(String name) throws RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
mRemote.transact(1, _data, _reply, 0);
_result = _reply.readInt();
} finally {......
}
return _result;
}
小结:server: 新建service,在service中创建binder,在service中实现业务逻辑函数;将service 添加到Androidmanifest.xml.Service manager start service.
client: bindService 查询Service manager获取binder;通过transact发送,server通过onTransact接收.
4 binder与共享内存的区别
linux的进程通信机制有管道,消息队列,共享内存,套接字。binder是andrid特有的进程通信机制.它是对共享内存做的改进.
- 效率:消息队列、Socket和管道,需要两次内核拷贝.Binder需要一次。使用copy_from_user将data写到内核.
copy_from_user是用户态内存拷贝到内核态内存.mmap不需要拷贝,用户态进程可以直接读写内核内存.
两个进程都使用mmap就是共享内存。一个使用copy_from_user,一个使用mmap就是binder,这样区分开了client和server. - 稳定性:上面说到共享内存的性能优于Binder,那为什么不采用共享内存呢?因为共享内存需要处理并发同步问题,容易出现死锁和资源竞争,稳定性较差。Binder基于C/S架构 ,Server端与Client端相对独立,稳定性较好。
共享内存是需要同步的,binder是cs结构不需要考虑共享内存的同步问题,client write之后read阻塞等待结果. - 安全性:传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份;而Binder机制为每个进程分配了UID/PID,且在Binder通信时会根据UID/PID进行有效性检测.
5 Aidl
QA: binder与aidl的关系 ?
aidl的底层是用binder实现的.有binder 跨进程通信,为什么还用aidl呢?因为aidl对binder做了封装,使用方便。
5.1 aidl的原理:只要按照规范写一个.aidl文件,编译之后自动创建一个与aidl同名的.java文件,里面已经帮我们自动写好一堆东东,这些标准化的东西,用模板自动生成即可,让开发者尽量关注功能实现上.
真是实现通信的是aidl编译之后,自动生成的java文件.java文件就是自动写好的binder使用框架.
aidl一定记住一点:aidl编译出来的stub就是binder(实际是binder的子类,proxy是stub的内部类)
5.2 aidl 结构图
代理模式示意图如下,aidl就属于代理模式.
aidl示意图如下,aidl就是统一接口,stubproxy 是proxy,stub是subjecImpl. binder没有统一接口.
5.3 aidl使用
整体过程:aidl定义并编译生成文件,Server service 注册,Server service的binder类实现接口函数(通常用匿名了实现),client bindService,client 调用接口.
注意:aidl接口实现是在service的binder类中实现的,不是service中.较少在service实现,然后使用binder调用service再调用service的业务逻辑函数.
Android:远程服务Service(含AIDL & IPC讲解)https://www.jianshu.com/p/34326751b2c6.这篇blog已经将的很好了
1) 定义aidl
与java接口的定义很类似.比如:
interface MyAidl{
int getId();
void setId(int id);
}
- server实现被调用的Remote Service(提供可实例化的Stub对象)
public class TaskService extends Service {
......
private finalITaskService.Stub mTaskServiceBinder =new ITaskService.Stub() {
public int getPid() {
return Process.myPid();
}
};
在AndroidManifest.xml文件里将Service声明出来,让系统里其他部分可以调用到.这个就是向service Manager注册binder(binder在service中).
- client bind Service&调用Remote Service
bindService的回调mTaskConnection获取stub.proxy. mTaskService.getPid的本质是stub.proxy通过binder 的transact发送,server 通过onTransact接收.
bindService(new Intent(ITaskService.class.getName()),mTaskConnection,
Context.BIND_AUTO_CREATE);
mPid = mTaskService.getPid();
5.4 aidl生成的stub 类(java binder 类)解析
aidl使用流程示意图:
call ladder:bindService获取iBinder(server onBind()返回)---stub.asinterface由iBinder获取stub.proxy---调用binder中的实现函数(底层bindler机制是,调用核心函数transact()发送,然后onTransact接收).
这个blog:https://www.jianshu.com/p/375e3873b1f4的 ”IMyAidlInterface .java类分析“部分,对aidl生成的stub类做了解析,比如:asinterface就是获取stub.proxy。
stub 类生成和作用:stub类是编译aidl自动生成的,既作为服务器端接口也作为Binder驱动给客户端使用
角色 | 作用 | 备注 |
---|---|---|
BookManager.Stub | server继承stub,实现业务逻辑函数 | binder |
BookManager.Stub.Proxy | 给client提供的binder接口 | binderproxy. proxy 是stub的内部类. |
binderProxy.BookManager.Stub.Proxy =BookManager.Stub
(代理-桩的设计理念),前者放在client,后者在server客户端最终通过这个类与服务端进行通信 |
**小结:
service,bindler,aidl什么关系?
server service仅仅用于注册,是server对外接口. service中创建binder,在binder中实现aidl中定义(或者server定义)的业务逻辑函数函数.
真正跨进程send/receive的是binder,实现函数也是在binder中,不是service.
aidl等于定义binder的接口,自动生成binder class。
附录: binder协议实现client使用stub.proxy,客户端接口会获得Binder驱动,调用其transact()发送消息至服务器.
server会接收Binder驱动发送的消息,收到消息后会执行Binder对象中的onTransact()函数,并按照该函数的参数执行不同的服务器端代码.
这节可有可无,binder实现有一套自己的协议,不需要知道具体内容,不需要重复造轮子。完全是为了满足读者的好奇心。
总结: 本文主要是对开篇的binder blog补充了,设计和实现。对aidl blog补充了源码分析。使读者了解binder和aidl更容易和全面一些.
aidl的使用最主要.aidl的本质是自动生成stub即binder类的使用。
binder和service是绑定的,binder对象是server service类的一个成员变量。注册service就是注册binder.
binder原理是优化的共享内存。
binder使用transact,onTransact 发送和接收.
伪代码:
client: stub.proxy是binderProxy
binderProxy.transact(sendmsg,parm1,parm2...);//发送
server:stub:
onTransact //接收
case sendmsg //不同函数
sendmsg(parm1,parm2...);//调用真正server service的实现。
附录:Android跨进程通信IPC之11——AIDL https://www.jianshu.com/p/375e3873b1f4
Android跨进程通信IPC之6——Binder框架:https://www.jianshu.com/p/b4a8be5c6300