最近在复习Android跨进程调用,整理了一下自己对Binder的一些理解,希望能对大家也有所帮助,如有错误欢迎指正~
为了加深理解,希望看完后能自己操练验证一下,要不真的很容易眼高手低哦
ps:不想细看,可以只看里面的3张图片即可~ ^_^
好了,回归主题,我们现在写个AIDL文件:
#IController.aidl
// Declare any non-default types here with import statements
interface IController {
void showMessage(String msg);
}
我们先来看一下默认生成文件的注释:
意思是说:如果不是默认类型,需要进行相应的import,类似java导包一样
那默认类型有哪些呢?
int、long、boolean、float、double、String、in/out/inout List
而Parcelable的实现类、aidl接口类
这个是需要进行import的
添加完AIDL文件后,我们编译一下,然后找到IController.java类,这个类是系统帮我们把AIDL文件转化为一个Java类,便于我们进行使用,当然我们自己也可以写~~
我们来看一下这个类的代码:
public interface IController extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.yukai.test.IController {
private static final java.lang.String DESCRIPTOR = "com.example.yukai.test.IController";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.yukai.test.IController interface,
* generating a proxy if needed.
*/
public static com.example.yukai.test.IController asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.yukai.test.IController))) {
return ((com.example.yukai.test.IController) iin);
}
return new com.example.yukai.test.IController.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_showMessage: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this.showMessage(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.yukai.test.IController {
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 void showMessage(java.lang.String msg) 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.writeString(msg);
mRemote.transact(Stub.TRANSACTION_showMessage, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_showMessage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public void showMessage(java.lang.String msg) throws android.os.RemoteException;
}
这是格式化之后的样子,我对这个类做了一个初步的结构图绘制如下:
可以比较明显的看到类的嵌套结构,一个接口类:IController,里面包含了接口方法(即我们声明的那个方法)和 一个Stub类:
为屏蔽客户调用远程主机上的对象,必须提供某种方式来模拟本地对象,这种本地对象称为存根(stub),存根负责接收本地方法调用,并将它们委派给各自的具体实现对象
我觉得上面这个解释还可以,基本表达了Stub的作用~
看完上面的图,觉得还是有点乱,既然是static class 静态内部类,那其实我们可以把里面的class抽离出来,作为一个新的文件,参考下图结构:
其中IController、Stub、Proxy类可以看成三个独立的类/接口
继承关系图也可以很清晰的看出来,那这三者是如何相互合作使用的呢?下面这个图可以很好的表达出具体的Activity与本地/远程Service交互逻辑和对应上面几个类的使用:
图注:
左面的Service是跟Activity处于同一个进程下的
右面的Service是跟Activity非同一个进程下的,可能是remote或其他
“所以测试的时候需要分别测这两种情况,有process:remote和什么都不写默认进程的。”
1.与当前进程的Service交互
在bind Service后,onServiceConnected会返回对应的binder对象,我们可以调试发现这个对象跟Service里面的binder对象其实是 同一个对象,因此我们可以直接转成我们Service的Binder,然后直接调用;当然如果用的是AIDL的方式,我们可以调用Stub的静态方法:asInterface,可以做相应的转化。
说到这里,我们还需要看一下这个asInterface方法实现,先只看一半:
public static com.test.IController asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.test.IController))) {
return ((com.test.IController) iin);
}
--------other return-------
}
关键在这里:
queryLocalInterface,查看源码可以发现,里面判断的是参数的IBinder对象的描述符是否是DESCRIPTOR,如果是,就会返回IController这个owner对象,所以就可以进行相应的强转并返回。
那remote返回什么呢?那看一下第二条吧~
2.与remote进程的Service交互
我们来看一下上面省略的那行代码:
return new com.test.IController.Stub.Proxy(obj);
看到这里原来返回的是个Proxy对象,那个obj会是什么呢?通过调试发现类名是BinderProxy对象,这个类我们的SDK里看不到,但是我们能在源码里找到,发现这个类是也是继承IBinder类,这个类负责与底层binder进行传递数据,然后会调用远程的Stub.onTransact,进行真实的Service方法调用。参照图片可以更好的理解一下~
3. 对应的Service与Activity进行交互
我们可以通过RemoteCallbackList这个类进行相关注册并进行回调,这个实现起来像是Activity是服务端,Service是客户端,大家可以实现一下看看~ 发现Stub是在Activity里新建并传递给Service端,由Service进行相应回调。当然还可以通过其他方式,比如广播(LocalBroadcast)也是可以的~~
就先写到这,如果有不对的地方,欢迎指正,多谢~~