Android进程间通信(一):使用Parcel对象完成进程间通信

一 Parcel类

1.1 官方定义:
Container for a message (data and object references) that can be sent through an IBinder. A Parcel can contain both flattened data that will be unflattened on the other side of the IPC (using the various methods here for writing specific types, or the general Parcelable interface), and references to live IBinder objects that will result in the other side receiving a proxy IBinder connected with the original IBinder in the Parcel.

Parcel就是一个存放读取数据的容器, Android系统中的binder进程间通信(IPC)就使用了Parcel类来进行客户端与服务端数据的交互,而且AIDL的数据也是通过Parcel来交互的。

1.2 继承关系:
public final class Parcel extends Object 
1.3 Parcel类实现进程间通信最关键的一个方法ontransact():

ontransact()是Binder机制中的客户端与服务器端进行实际操作的方法,由客户端获取到服务器的Ibinder来调用:

public boolean transact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException;


//参数说明:   
// code :是请求的ID号    
// data :客户端请求发送的参数   
// reply:服务器端返回的结果   
// flags:一些额外的标识,如FLAG_ONEWAY等,通常为0.  
1.4 被调用的流程如下:

简单的客户端与服务端之间的调用如图所示:

这里写图片描述

//1 在客户端创建一个IBinder 接口对象
private IBinder mPlusBinder;
//2 将客户端的IBinder 接口对象与服务端的MyBinder进行关联(说法不太妥当,其实当调用bindService之后,mPlusBinder就是服务端的对象了。)
 @Override
        public void onServiceConnected(ComponentName name, IBinder service){
            mPlusBinder = service;
        }
//3 执行transact()方法
mPlusBinder.transact(0x110, data, reply, 0);
//4 回调mPlusBinder 的onTransact()方法
 @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        }
1.5 基本方法

从中我们可以看到Parcel的重要性以及窥探它的使用情况,接下来,我主要分析它的基本方法。

常用方法介绍:
        obtain()  获得一个新的parcel ,相当于new一个对象
        dataSize()  得到当前parcel对象的实际存储空间
        dataCapacity()  得到当前parcel对象的已分配的存储空间, >=dataSize()值  (以空间换时间)
        dataPostion()   获得当前parcel对象的偏移量(类似于文件流指针的偏移量)
        setDataPosition()  设置偏移量
        recyle()  清空、回收parcel对象的内存

        writeInt(int)   写入一个整数
        writeFloat(float)   写入一个浮点数
        writeDouble(double)   写入一个双精度数
        writeString(string)   写入一个字符串

     当然,还有更多的writeXXX()方法,与之对应的就是readXXX(),具体方法请参阅SDK。其中几个值得注意的方法为:
         writeException() 在Parcel队头写入一个异常
         writeNoException()  Parcel队头写入“无异常“
         readException() 在Parcel队头读取,若读取值为异常,则抛出该异常;否则,程序正常运行。
1.6 Parcel对象的存放空间图:

这里写图片描述

二 在进程间传递Parcel对象实现进程间通信:

2.1 DEMO描述如下:
1 在服务端CalcPlusService 实现两个整数的相乘和相除,并将结果返回;
2 在客户端MainActivity获取用户输入的参数,并传递到服务端;
2.2 计算服务–CalcPlusService .java
public class CalcPlusService  extends Service{

    private static final String DESCRIPTOR = "CalcPlusService";
    private MyBinder binder=new MyBinder();
    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("Server","onCreate()");
    }



    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i("Server","onBind()");
        return binder;//当客户端调用bindService时此方法 会被调用,并且返回客户端一个binder对象;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i("Server","onUnbind()");
        return super.onUnbind(intent);//当客户端调用unbindService时此方法 会被调用;
    }

    @Override
    public void onRebind(Intent intent) {
        super.onRebind(intent);
        Log.i("Server","onRebind()");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i("Server","onDestroy()");
    }

    private class MyBinder extends Binder {//自定义Binder,继承Binder类,【class Binder implements IBinder】,实现onTransact方法;
        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            int arg0;
            int arg1;
            int result;
            data.enforceInterface(DESCRIPTOR); //指定服务的标识为CalcPlusService
            arg0 = data.readInt();//读取一个整数
            arg1 = data.readInt();

            switch (code){
                case 0x110:{
                    result = arg0 * arg1;
                    reply.writeNoException();//Parcel队头写入“无异常“
                    reply.writeInt(result);//写入一个整数
                    return true;
                }
                case 0x111:{
                    result = arg0 / arg1;
                    reply.writeNoException();//Parcel队头写入“无异常“
                    reply.writeInt(result);//写入一个整数
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
    }
}
2.3 然后在清单文件中注册服务,并新指定一个remote进程:
 <service android:name=".CalcPlusService"
            android:process=".remote">
        </service>
2.4 客户端代码MainActivity.java:
public class MainActivity extends AppCompatActivity {

    private IBinder mPlusBinder;//进程间通信Parcel对象的传递主要靠IBinder 接口;
    private EditText et1,et2;
    private ServiceConnection mServiceConnPlus = new ServiceConnection(){//ServiceConnection 是一个接口,是客户端与服务端进行连接的一个桥梁;
        @Override
        public void onServiceConnected(ComponentName name, IBinder service){
            Log.i("Server", " mServiceConnPlus onServiceConnected");
            mPlusBinder = service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name){
            Log.i("Server", "mServiceConnPlus onServiceDisconnected");
            mPlusBinder=null;
            //关于service://
            //(1)取消绑定仅需要使用unbindService()方法,并将ServiceConnnection传递给unbindService()方法。
            //(2)需注意的是,unbindService()方法成功后,系统并不会调用onServiceDisconnected(),因为onServiceDisconnected()仅在意外断开绑定时才被调用; 
            //(3)当bindService后,不能stopService,需要通过unBindService()来解除绑定, 
            //(4)startService()后,不可以通过unBindService()来销毁service;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et1=(EditText)findViewById(R.id.et1);
        et2=(EditText)findViewById(R.id.et2);
    }

    public void bindService(View view){
        Intent intent = new Intent(this, CalcPlusService.class);
        boolean plus=bindService(intent, mServiceConnPlus, Context.BIND_AUTO_CREATE);//标志位BIND_AUTO_CREATE:automatically create the service as long as the binding exists.
        Toast.makeText(view.getContext(), "绑定服务"+plus, Toast.LENGTH_SHORT).show();
    }

    public void unbindService(View view){
        try {
            unbindService(mServiceConnPlus);
            mPlusBinder=null;//这里必须代码置空
            Toast.makeText(view.getContext(), "解绑成功", Toast.LENGTH_SHORT).show();
        } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(view.getContext(), "未绑定", Toast.LENGTH_SHORT).show();
        }
    }

    /**乘法运算*/
    public void mulInvoked(View view){
        if(!inspectData()){
            return;
        }
        if (mPlusBinder == null){
            Toast.makeText(this, "未连接服务端或服务端被异常杀死", Toast.LENGTH_SHORT).show();
        } else{
            android.os.Parcel data = android.os.Parcel.obtain();//Parcel.obtain():获得一个新的parcel ,相当于new一个对象;
            android.os.Parcel reply = android.os.Parcel.obtain();
            int result;
            try{
                data.writeInterfaceToken("CalcPlusService");//写入服务的标志,与enforceInterface()配套使用;
                data.writeInt(Integer.parseInt(et1.getText().toString()));
                data.writeInt(Integer.parseInt(et2.getText().toString()));
                mPlusBinder.transact(0x110, data, reply, 0);//transact(int code, Parcel data, Parcel reply, int flags)
                reply.readException();//在Parcel队头读取,若读取值为异常,则抛出该异常;否则,程序正常运行。
                result = reply.readInt();
                Toast.makeText(this, result + "", Toast.LENGTH_SHORT).show();
            } catch (RemoteException e){
                e.printStackTrace();
            } finally{
                reply.recycle();
                data.recycle();
            }
        }
    }

    /**除法运算*/
    public void divInvoked(View view){
        if(!inspectData()){
            return;
        }
        if (mPlusBinder == null){
            Toast.makeText(this, "未连接服务端或服务端被异常杀死", Toast.LENGTH_SHORT).show();
        } else{
            android.os.Parcel data = android.os.Parcel.obtain();
            android.os.Parcel reply = android.os.Parcel.obtain();
            int result;
            try{
                data.writeInterfaceToken("CalcPlusService");//通过Parcel::writeInterfaceToken往data写入一个RPC头
                data.writeInt(Integer.parseInt(et1.getText().toString()));
                data.writeInt(Integer.parseInt(et2.getText().toString()));
                mPlusBinder.transact(0x111, data, reply, 0);
                reply.readException();
                result = reply.readInt();
                Toast.makeText(this, result + "", Toast.LENGTH_SHORT).show();
            } catch (RemoteException e){
                e.printStackTrace();
            } finally{
                reply.recycle();
                data.recycle();
            }
        }
    }

    private boolean inspectData(){
        if(TextUtils.isEmpty(et1.getText().toString())||TextUtils.isEmpty(et2.getText().toString())){
            Toast.makeText(this, "请填全两个参数!", Toast.LENGTH_SHORT).show();
            return false;
        }
        return true;
    }
}
2.5 运行结果如下:

这里写图片描述

三 总结:

通过本DEMO应该掌握以下几点:
(1)服务端自定义Binder类,实现其onTransact()方法;
(2)客户端使用ServiceConnection接口对象的使用,以及在客户端发起bindService(“三个参数的含义”);
(3)了解客户端IBinder参与的行为,以及调用transact(int code, Parcel data, Parcel reply, int flags)方法;
(4)了解Parcel 对象及基本的write/read等方法;

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值