引言
上一篇文章分析了AIDL的调用流程,这篇文章将抛弃传统的AIDL使用方式,通过Binder的方式进行通信。
跨进程通信时,客户端通过代理类IJustKiddingProxy的add()方法调用,得到计算结果,那是不是可以不通过原始AIDL的方式来通信呢?
答案是肯定的,不然也不会有这篇文章了。
通过Binder调用AIDL接口
@Override
public int add(int a, int b) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(IJustKidding.DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
首先来看看通过Binder的方式调用需要哪些条件
- IJustKidding.DESCRIPTOR,接口的描述字符,很容易得到,就是"com.test.BinderTest.IJustKidding"。
- TRANSACTION_add,该方法在接口中的顺序标识符,IBinder.FIRST_CALL_TRANSACTION,查看IBinder.java源码,为1。
- mRemote,驱动返回的IBinder句柄,如果不通过bindServer()绑定服务去获取,好像拿不到???
这里要用到ServiceManager的功能,系统服务在启动时,会通过ServiceManager去注册,跨进程调用时,再通过ServiceManager去拿到对应服务的IBinder句柄。
于是,我们可以在JustKiddingService服务里面像ServiceManager进行注册:
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class JustKiddingService extends Service {
//JustKiddingNative相当于IJustKidding.Stub
private final JustKiddingNative mBinder = new JustKiddingNative() {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
@Override
public int sub(int a, int b) throws RemoteException {
return a - b;
}
};
public JustKiddingService() {
addService();
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mBinder;
}
private boolean addService() {
try {
final Class<?> systemPropertyClass = Class.forName("android.os.ServiceManager");
Method method = systemPropertyClass.getMethod("addService", String.class, Class.forName("android.os.IBinder"));
//justkidding_server是JustKiddingService服务对应的字符串
method.invoke(null, "justkidding_server", (IBinder) mBinder);
return true;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return false;
}
}
调用端:
private IBinder getService(String server){
Log.d("***", "BinderTestActivity -> getService()");
try {
final Class<?> systemPropertyClass = Class.forName("android.os.ServiceManager");
Method method = systemPropertyClass.getMethod("getService", String.class);
IBinder binder = (IBinder) method.invoke(null, server);
return binder;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
IBinder mPlusBinder = getService("justkidding_server");
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result = -1;
try {
_data.writeInterfaceToken("com.test.BinderTest.IJustKidding");
_data.writeInt(2);
_data.writeInt(1);
mPlusBinder.transact(1, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} catch (RemoteException e) {
e.printStackTrace();
} finally {
_reply.recycle();
_data.recycle();
}
Log.d("***", "add = " + _result);
记住这里的apk签名时要使用系统签名,不然会报错,权限异常。
ServiceManager: add_service('justkidding_server',aa) uid=10053 - PERMISSION DENIED
系统签名之后,抓日志看运行结果:
07-12 17:00:24.658 D/*** (12409): BinderTestActivity -> getService()
07-12 17:00:24.662 D/*** (12477): Stub --> onTransact() TRANSACTION_add result = 3
07-12 17:00:24.663 D/*** (12409): add = 3
至此,通过Binder的方式跨进程通信成功。
该方法省略了bindService()的步骤,不需要接口文件,更加灵活简洁;
缺点是需要系统签名。