aidl ( 六) 加入序列化对象传输

在之前的文章中,我们传的都是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);
}
}

 

 

 

 

 

 

 

你可能感兴趣的:(android_Aidl)