在之前的文章中,我们传的都是java基本类型或者string,其实aidl也可以传序列化好的对象,序列化 的对象必须实现Parcelable接口。
我们来举个例子说明aidl如何传输序列化数据的,参考module为aidlclient3和aidlserver3。aidlclient3在aidlclient1的基础上修改,aidlserver3在aidlserver2 的基础上修改。
先看客户端,首先在com.data包下建立BaseData类,BaseData实现了Parcelable接口,然后在aidl文件夹的同名目录下建立,BaseData.aidl,代码如下,主要就是申明这个类。
// BaseData.aidl
package com.data;
// Declare any non-default types here with import statements
parcelable BaseData;
然后在IMyAidlInterface.aidl文件内加一行代码
int putRemote(in BaseData data,int t);
注意BaseData必须加方向tag,是in或者out或者inout,记住,只要不是基本类型或者string都必须加方向tag。
注意要加BaseData的import代码
import com.data.BaseData;
然后在onServiceConnected内加入
BaseData baseData=new BaseData("a","b",5);
myAIDLInterface.putRemote(baseData, 7);
LogUtil.fish("now name is " + baseData.getName());
OK,客户端代码搞定
此时,客户端代码如下
public class MainActivity extends Activity implements View.OnClickListener {
private Button bindService;
private Button unbindService;
private IMyAidlInterface myAIDLInterface;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
LogUtil.d("handleMessage");
TextView tv = (TextView) findViewById(R.id.textView);
tv.setText("" + msg.what);
}
};
private ICallback callback = new ICallback.Stub() {
@Override
public void showTime(int x) throws RemoteException {
LogUtil.d("callback" + x);
LogUtil.process(getApplicationContext());
LogUtil.thread("showTime");
handler.sendEmptyMessage(x);
//
}
};
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
LogUtil.d("process name" + SystemUtil.getCurProcessName(MainActivity.this));
myAIDLInterface = IMyAidlInterface.Stub.asInterface(service);
LogUtil.thread("onServiceConnected");
try {
int cnt = myAIDLInterface.getCount();
// double db = myAIDLInterface.complexCal("hello world", 6);
LogUtil.d("result is " + cnt);
// LogUtil.d("complexCal value " + db);
BaseData baseData=new BaseData("a","b",5);
myAIDLInterface.putRemote(baseData, 7);
LogUtil.fish("now name is " + baseData.getName());
myAIDLInterface.setCallback(callback);
// myAIDLInterface.setCallback(new ICallback.Stub() {
// @Override
// public void showTime(int x) throws RemoteException {
// LogUtil.d("callback"+x);
//// TextView tv = (TextView) findViewById(R.id.textView);
//// tv.setText(""+x);
// }
//
// @Override
// public IBinder asBinder() {
// return null;
// }
// });
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
public String mAction="com.example.servicetest.MyAIDLService";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LogUtil.thread("main");
bindService = (Button) findViewById(R.id.bind_service);
unbindService = (Button) findViewById(R.id.unbind_service);
bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
Intent intent = new Intent(mAction);
startService(intent);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bind_service:
Intent intent = new Intent(mAction);
boolean b = bindService(intent, connection, Service.BIND_AUTO_CREATE);
LogUtil.d("bind result " + b);
break;
case R.id.unbind_service:
unbindService(connection);
LogUtil.d("unbind ");
break;
default:
break;
}
}
@Override
public void onBackPressed() {
super.onBackPressed();
Intent intent = new Intent(mAction);
LogUtil.d("stopservice");
stopService(intent);
}
}
而服务端的service代码如下,
public class MyService extends Service {
private boolean quit;
private int count;
public static final int TIMER = 1;
private ICallback mCallback;
private IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
@Override
public int getCount() throws RemoteException {
return count;
}
@Override
public double complexCal(String str, int t) throws RemoteException {
int ret = str.hashCode() + t;
return 1 * 0.3;
}
@Override
public int putRemote(BaseData data, int t) throws RemoteException {
String s=data.getName();
data.setName("sss");
LogUtil.fish(s);
return t+1;
}
@Override
public void setCallback(ICallback call) throws RemoteException {
LogUtil.d("setcallback" + call);
LogUtil.thread("set callback");
mCallback = call;
}
};
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case TIMER:
if(!quit)
{
count++;
if (mCallback != null) {
// LogUtil.d("callback");
// try {
// mCallback.showTime(count);
// } catch (RemoteException e) {
// e.printStackTrace();
// }
} else {
LogUtil.d("callback null");
}
sendEmptyMessageDelayed(TIMER,2000);
}
break;
default:
super.handleMessage(msg);
}
}
};
@Override
public void onCreate() {
super.onCreate();
LogUtil.thread("service main");
LogUtil.d("onCreate process name " + SystemUtil.getCurProcessName(MyService.this));
LogUtil.d("Service is Created");
count=0;
handler.sendEmptyMessageDelayed(TIMER,2000);
}
@Override
public void onDestroy() {
LogUtil.d("onDestroy process name " + SystemUtil.getCurProcessName(MyService.this));
quit = true;
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
LogUtil.d("onBind process name " + SystemUtil.getCurProcessName(MyService.this));
return mBinder;
}
}
此时clent可以成功把数据传给server。
我们注意下这行代码
myAIDLInterface.putRemote(baseData, 7);
,baseData传的是引用,那么在服务端能不能修改baseData呢,答案是可以的,但是aidl的方向tag必须 写为inout,而且BaseData类内部要加一个函数
public void readFromParcel(Parcel in){
name = in.readString();
payType = in.readString();
payMoney = in.readInt();
}
具体的原理是这样的,baseData在跨进程的传输过程中是要经过序列化,修改,反序列化的过程的。如果方向tag为in,那表示这个数据需要从客户端传给服务端,那只要客户端序列化好了,传给服务端就好了。
过程是这样的,
1,客户端把baseData序列化为字节数组
2,字节数组传到服务端
3,服务端利用BaseData里的CREATOR把字节数组转化为BaseData实例。
但是如果tag为inout,那表示这个数据一方面需要传给服务端,另一方面还需要服务端返回给客户端。
过程除了上面3步之外,还要执行
4、服务端处理完之后把服务端的BaseData序列化成字节数组,
5、客户端使用readFromParcel,读取字节数组,生成新的BaseData,覆盖原有对象。
由此可见方向tag非常重要,会很大程度影响效率,所以如果客户端传给服务端的数据,而不需要服务端写好传回来就用in。
如果一定要服务端处理好后传给客户端,就用inout。
可以简单理解为in是传值,inout是传引用。只是理解哦,这里是跨进程的
in的效率比inout高出很多。
从代码的角度上,我们可以看加in和inout编译出来的java文件的区别,
in的服务端代码
case TRANSACTION_putRemote:
{
data.enforceInterface(DESCRIPTOR);
com.c.BaseData _arg0;
if ((0!=data.readInt())) {
_arg0 = com.c.BaseData.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
int _arg1;
_arg1 = data.readInt();
int _result = this.putRemote(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
if ((_arg0!=null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
inout生成 的服务端代码
case TRANSACTION_putRemote:
{
data.enforceInterface(DESCRIPTOR);
com.c.BaseData _arg0;
if ((0!=data.readInt())) {
_arg0 = com.c.BaseData.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
int _arg1;
_arg1 = data.readInt();
int _result = this.putRemote(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
差别在于,inout的服务端需要把arg0以parceble形式写到reply里面去,准备回传给客户端
if ((_arg0!=null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
再来看in生成的客户端代码
@Override public int putRemote(com.c.BaseData data, int t) 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);
if ((data!=null)) {
_data.writeInt(1);
data.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
_data.writeInt(t);
mRemote.transact(Stub.TRANSACTION_putRemote, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
再看inout生成的客户端代码
@Override public int putRemote(com.c.BaseData data, int t) 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);
if ((data!=null)) {
_data.writeInt(1);
data.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
_data.writeInt(t);
mRemote.transact(Stub.TRANSACTION_putRemote, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
if ((0!=_reply.readInt())) {
data.readFromParcel(_reply);
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
比较一下,差距在于,inout的客户端需要从reply中读取BaseData数据,并且写入putRemote传进来的参数data中
if ((0!=_reply.readInt())) {
data.readFromParcel(_reply);
}
}