AIDL in out inout
in out inout 区别
他们都用来修饰传入参数, 区别如下
-
in: 对象中的变量可以从客户端传到服务端, 而服务端对该对象的修改不会影响到客户端. -
out: 对象中的变量无法从客户端传到服务端, 服务端收到的是一个内部变量全为初始值的变量, 但是服务端对该对象的的修改可以影响到客户端 -
inout: 对象中的变量既可以传到服务端, 服务队对其的修改也可以影响到客户端
具体原理
一个可以通过 Binder 传递的对象
-
必须 要实现
Parcelable接口. 即必须要重写writeToParcel(Parcel, int), 重写了该方法, 说明该对象有in的能力了 -
可选 可以添加一个
readFromParcel(Parcel)方法, 添加了该方法, 说明该对象有out的能力. 如果没有添加该方法, 却用out或者inout修饰了, 会报错.
IAidlListener.aidl 的定义: 很简单的一个方法 putIPCBean(inout IPCBean io, in IPCBean i, out IPCBean o);
上代码:
IAidlInterface.Stub.Proxy 客户端的 服务端代理类
@Override public void putIPCBean(com.example.myapplication.bean.IPCBean inout, com.example.myapplication.bean.IPCBean in, com.example.myapplication.bean.IPCBean out) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
//region inout in, 会将数据写入到 Parcel, 也可以说明 out 不会将对象传递过去
if ((inout!=null)) {
_data.writeInt(1);
inout.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
if ((in!=null)) {
_data.writeInt(1);
in.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
//endregion
//IPC
boolean _status = mRemote.transact(Stub.TRANSACTION_putIPCBean, _data, _reply, 0);
_reply.readException();
// 数据写回到对象
//region inout, out 会调用 readFromParcel(Parcel) 方法来根据服务端返回值修改内部变量
// 这就是没有添加该方法的类 如果用 inout, out 修饰会报错的原因
if ((0!=_reply.readInt())) {
inout.readFromParcel(_reply);
}
if ((0!=_reply.readInt())) {
out.readFromParcel(_reply);
}
//endregion
}
finally {
_reply.recycle();
_data.recycle();
}
}
长求总
从客户端来看:
-
被
out修饰的对象不会调用writeToParcel(Parcel, int), 即不会写入到 Parcel 中, 所以服务端不会收到该对象. -
被
inout,out修饰的对象会调用readFromParcel(Parcel), 即根据服务端的改动, 修改自己的内部变量. 这就是没有添加该方法的类 如果用 inout, out 修饰会报错的原因.
IAidlInterface.Stub 即服务端的实现
// 变量名已经修改为对应数据的流向了
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
// ... 省略其他不相干的
switch (code)
{
case TRANSACTION_putIPCBean:
{
data.enforceInterface(descriptor);
//region inout, in, 可以看到都是从 客户端传过来的 Parcel 中生成对象的,
// 即客户端中对象的变量 可以传到服务端
com.example.myapplication.bean.IPCBean inout;
if ((0!=data.readInt())) {
inout = com.example.myapplication.bean.IPCBean.CREATOR.createFromParcel(data);
}
else {
inout = null;
}
com.example.myapplication.bean.IPCBean in;
if ((0!=data.readInt())) {
in = com.example.myapplication.bean.IPCBean.CREATOR.createFromParcel(data);
}
else {
in = null;
}
//endregion
//region out, 直接构造对象, 所以内部变量都是初始值
com.example.myapplication.bean.IPCBean out;
out = new com.example.myapplication.bean.IPCBean();
//endregion
this.putIPCBean(inout, in, out);
// 数据写回到客户端
reply.writeNoException();
//region inout, out 传入的对象在服务端的改动, 会写回到 Parcel 中, 可以传递回客户端
if ((inout!=null)) {
reply.writeInt(1);
inout.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
if ((out!=null)) {
reply.writeInt(1);
out.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
//endregion
return true;
}
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
长求总
从服务端来看:
-
在构造对象时, 只会把被
ininout修饰的对象构建出来. (因为用out修饰的对象 没有写入Parcel, 上文提到了). -
在写入 reply 数据时, 只会调用被
inoutout修饰的对象的writeToParcel(Parcel, int)方法.