正文
android的Binder机制是IPC中非常重要的,可以说android中的绝大部分IPC都是用binder实现的,有句话叫“无binder,不android”
从framework角度来看,ServiceManager连接各种Manager(如WindowManager、ActivityManager等)的桥梁
从app层来看,是客户端服务端通信的媒介
往往通过编写aidl文件来实现。
aidl编译会生成一个java文件,本文以后边的代码为例来说明,后边的代码就是aidl文件编译生成的java文件,来源于AidlClient这个module中首先这个java文件是个接口,继承自android.os.IInterface,
aidl实际上是个中间文件,或者说android提供给大家的一种简单实现ipc的方式,本质上还是用到了这个接口类
然后这个接口内部主要由一个抽象类Stub,还有若干个函数,这些函数就是aidl文件中定义的函数,这是要进行ipc调用的函数.
主要3个类的关系如下图所示.
public class MyService extends Service {
......
@Override
public IBinder onBind(Intent intent) {
LogUtil.d("onBind process name " + SystemUtil.getCurProcessName(MyService.this));
return mBinder;
}
IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
@Override
public int getCount() throws RemoteException {
return count;
}
@Override
public double complexCal(String str, int t) throws RemoteException {
LogUtil.d("complexCal real " + SystemUtil.getCurProcessName(MyService.this));
int ret=str.hashCode()+t;
return ret*0.3;
}
};
}
一般来说我们的流程是bindservice,onServiceConnected,然后在onServiceConnected回调里进行IPC。常见代码像这样。
public void onServiceConnected(ComponentName name, IBinder service) {
myAIDLInterface = IMyAidlInterface.Stub.asInterface(service);
try {
int cnt = myAIDLInterface.getCount();
}
。。。
}
onServiceConnected回来的是binder的一个proxy,通过asInterface转化为Proxy,而Proxy就是客户端和服务端通信的中介。真正的binder在Service进程内,但是每个进程都可以去要一个代理,这是个CS结构。去bind 那个 service,就会得到一个代理。真正的binder只有一个,但是代理可以有很多个。
getCount方法如下,首先把数据写到_data,然后调mRemote的transact,会导致服务端的Binder调用onTransact调用。这就完成了IPC。
public int getCount() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getCount, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
再仔细看看 Stub是个啥?
public static abstract class Stub extends android.os.Binder implements com.aidl.server.IMyAidlInterface
注意他是个静态类,继承了Binder,而Binder实现了IBinder
Stub里面有神马?
一个asInterface,一个asBinder,一个onTransact,还有个Proxy类,这些都是干啥的
先看asInterface方法
public static com.aidl.server.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.aidl.server.IMyAidlInterface))) {
return ((com.aidl.server.IMyAidlInterface) iin);
}
return new com.aidl.server.IMyAidlInterface.Stub.Proxy(obj);
}
首先要有个客户端和服务端的概念,我们可以认为这是2个进程。service执行的地方时在服务端的binder线程池中,客户端就是bindService或者startService的那个进程。当然客户端和服务端可以是同一个进程,那我们还要IPC干嘛??岂不是多此一举?所以asInterface做了一件事情,他来判断一下,客户端进程是否和服务端进程是同一个进程,如果是就返回((com.aidl.server.IMyAidlInterface) iin),实际上就是返回Stub对象;而如果不在同一个进程就new 一个proxy对象,要走ipc通道。判断是否同一个进程,主要通过queryLocalInterface方法。
asBinder就是返回this指针,没啥好说的
onTransact,他运行在服务端的binder线程池中,等客户端发起跨进程调用的时候,会执行onTransact方法,来实现客户端数据和方法名读取,方法执行,存储结果等功能
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
code代表aidl里的某个方法的标识,对应这种static final int TRANSACTION_getCount = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
data代表客户端存进来的数据,是序列化的
reply是方法返回值存储的地方,也是序列化的
实际上进程之间传递的数据都是序列化的,双方约定好方法和数据和返回值怎么存储,就能进行准确的读和写
还剩个Proxy,跨进程调用通过他来实现,注意Proxy这个东西是属于客户端的
Proxy有什么?
看此处的例子,有成员变量mRemote,成员函数asBinder(),getCount(),complexCal()
mRemote是个IBinder对象,在前文的asInterface传入,代表了服务端binder对象,asBinder()就是返回mRemote
再看getCount,就是将函数名和参数序列化,然后调用mRemote.transact(Stub.TRANSACTION_getCount, _data, _reply, 0);
这个transact会调用服务端的onTransact,然后完成真正的函数执行,返回值存到_reply中
complexCal和getCount类似,不多说
这么看来最核心的,就是proxy内的transact调用了Binder内的onTransact,这里就是最核心的跨进程.注意跨进程调用的时候,客户端方会挂起当前线程,等待服务端处理数据,服务端处理完了会通知客户端。
而写服务端的时候要注意同步,有时候需要加synchronized锁,因为可能有几个客户端同时调用某个方法
代码
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/fish/AndroidStudioProjects/FishProject/AidlServer/src/main/aidl/com/aidl/server/IMyAidlInterface.aidl
*/
package com.aidl.server;
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.aidl.server.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.aidl.server.IMyAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.aidl.server.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.aidl.server.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.aidl.server.IMyAidlInterface))) {
return ((com.aidl.server.IMyAidlInterface) iin);
}
return new com.aidl.server.IMyAidlInterface.Stub.Proxy(obj);
}
@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 {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getCount: {
data.enforceInterface(DESCRIPTOR);
int _result = this.getCount();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_complexCal: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
int _arg1;
_arg1 = data.readInt();
double _result = this.complexCal(_arg0, _arg1);
reply.writeNoException();
reply.writeDouble(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.aidl.server.IMyAidlInterface {
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 int getCount() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getCount, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public double complexCal(java.lang.String str, int t) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
double _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(str);
_data.writeInt(t);
mRemote.transact(Stub.TRANSACTION_complexCal, _data, _reply, 0);
_reply.readException();
_result = _reply.readDouble();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getCount = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_complexCal = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public int getCount() throws android.os.RemoteException;
public double complexCal(java.lang.String str, int t) throws android.os.RemoteException;
}