Linxu 及 Android IPC
linxu进程间通信手段
- 管道 Pipe,在创建时分配一个page大小的内存,缓存区大小比较有限(拷贝2次)
- 信号 Signal,不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等
- 信号量 semaphore,常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段
- 套接字 Socket,作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信(拷贝2次)
- 共享内存 ,无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决
- 消息队列 Message,信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信(拷贝2次)
Android - Binder驱动
- Binder采用 C/S架构
- 选择理由:从性能、稳定性、安全性、语言层面4个角度综合考虑的结果
- 性能:Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,但共享内存方式一次内存拷贝都不需要;从性能角度看,Binder性能仅次于共享内存
- 稳定性:Binder是基于C/S架构的,简单解释下C/S架构,是指客户端(Client)和服务端(Server)组成的架构,Client端有什么需求,直接发送给Server端去完成,架构清晰明朗,Server端与Client端相对独立,稳定性较好;而共享内存实现方式复杂,没有客户与服务端之别, 需要充分考虑到访问临界资源的并发同步问题,否则可能会出现死锁等问题;从这稳定性角度看,Binder架构优越于共享内存
- 安全性:传统Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份;Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志,前面提到C/S架构,Android系统中对外只暴露Client端,Client端将任务发送给Server端,Server端会根据权限控制策略,判断UID/PID是否满足访问权限,目前权限控制很多时候是通过弹出权限询问对话框,让用户选择是否运行。传统IPC只能由用户在数据包里填入UID/PID;另外,可靠的身份标记只有由IPC机制本身在内核中添加。其次传统IPC访问接入点是开放的,无法建立私有通道。从安全角度,Binder的安全性更高。
- 语言层面:Linux是基于C语言(面向过程的语言),而Android是基于Java语言(面向对象的语句),而对于Binder恰恰也符合面向对象的思想,将进程间通信转化为通过对某个Binder对象的引用调用该对象的方法,而其独特之处在于Binder对象是一个可以跨进程引用的对象,它的实体位于一个进程中,而它的引用却遍布于系统的各个进程之中。Binder模糊了进程边界,淡化了进程间通信过程,从语言层面,Binder更适合基于面向对象语言的Android系统。
- 所有ipc都是通过driver传送
Android - 多进程
- 不同进程不能通过内存来达到共享数据,同一个类会产生副本(常见就是通过静态变量来传递)
- 静态变量与单例模式失效
- 线程同步机制失效
- sharedPreferences可靠性下降,底层读写cml,并发会有问题
- Application会创建多次,因为运行在同一进程的组件应该属于同一个虚拟机和同一个Application
定义AIDL接口
- 导入的包名
- 如果有使用Object对象,需要该对象 implement Parcelable 接口,并且需要导入该接口包名+类名;如果是primitive type就不需要,同时需要创建一个同名的aidl文件。所有的.adil文件已经需要传递对象接口,需要在Service和Client中各一份
- 创建一个aidl文件,定义暴露的方法
- 客户端需要实现:conn接口,拿到接口对象
- 服务端实现stub,实现抽象方法
获取aidl对象
获取手段,本地service
- 先启动服务
- startService启动,则还没有得到服务端Binder
- 绑定启动则能直接得到
- 取得ServiceConnection对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25public class MainActivity extends AppCompatActivity{
private IMyAidlInterface iMyAidlInterface;
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindService(new Intent("cc.abto.server"), new ServiceConnection() {
public void onServiceConnected(ComponentName name, IBinder service){
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
void onServiceDisconnected(ComponentName name){
}
}, BIND_AUTO_CREATE);
}
public void onClick(View view){
try {
Toast.makeText(MainActivity.this, iMyAidlInterface.getName(), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
通讯过程
- 服务端,Binder类对象
- 重载onTransact方法,用于存放数据,有4个参数
- int code,区分具体的功能
- Parcel data,客户端发送的数据
- Parcel reply,服务端回复的数据
- int flags 约定双发是否进行双向通讯
- 接收Binder驱动发送的消息,执行onTransact方法
- 重载onTransact方法,用于存放数据,有4个参数
- 驱动端
- 服务端在创建Binder对象时驱动端会创建对应的mRemote对象,也是Binder对象
- 客户端
- 重载transact方法,同样也有4个参数
- 获取服务端在驱动端对应的mRemote对象,并调用transact方法
1 | public interface IBinder { |
aidl接口生成 Stub
1 | interface IMyAidlInterface { |
生成的实现IInterface类
- stub内部类
- asInterface静态方法
- onTransact方法,读取参数,写入值,根据code值读取每个方法对应的解析规则
- proxy内部类
- 每一个接口定义的方法,写入参数,读取值,调用,mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100public interface IMyAidlInterface extends android.os.IInterface{
//生成的stub内部类
public static abstract class Stub extends android.os.Binder implements com.example.code.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.example.code.IMyAidlInterface";
public Stub(){
this.attachInterface(this, DESCRIPTOR);
}
//用户外部service获取
public static com.example.code.IMyAidlInterface asInterface(android.os.IBinder obj){
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.code.IMyAidlInterface))) {
return ((com.example.code.IMyAidlInterface)iin);
}
return new com.example.code.IMyAidlInterface.Stub.Proxy(obj);
}
public android.os.IBinder asBinder(){
return this;
}
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{
switch (code){
case INTERFACE_TRANSACTION:{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_basicTypes:{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.code.IMyAidlInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
public android.os.IBinder asBinder(){
return mRemote;
}
public java.lang.String getInterfaceDescriptor(){
return DESCRIPTOR;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) 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.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
}finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}
- 每一个接口定义的方法,写入参数,读取值,调用,mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0)