模仿AIDL实现进程间通信

前言

此篇模仿AIDL实现进程间通信,如果对进程间通信的整个流程不熟悉可以去看上篇《Binder学习心得》这篇文章,在这篇文章做了个总结,(他与此篇文章相辅相成,由于太长了,不得不分开,不然看着也累)。然后结合此篇文章可以加深对进程间通信的印象。文中如有出现错误,谢谢留言指正,避免其他同学入坑。

进程A,也就是应用A的代码

首先在A里面写一个服务类,MyService继承这个Service就成了服务了,根据下面代码可以看出重写了onBind这个方法,只要集成这个Service就必须得实现这个方法。

 public class MyService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return myBinder;
    }

    //清单文件
    <service android:name=".MyService">
        <intent-filter>
            <action android:name="www.bjp.ftz.com" />//这个是B应用绑定服务的依据
        </intent-filter>
    </service>

为什么呢?我们来看下这个Service类。

 public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
        @Nullable
        public abstract IBinder onBind(Intent intent);
    }

从上面片段代码可以看出,Service是个抽象类,其onBind是个抽象方法,因此,继承Service这个类就必须实现onBind这个方法,那么这个方法是干什么用的呢?我们都知道startService和bindService的区别。因此,startservice开启服务是不会调用onbind的。当我们调用bindService方法的时候那么onBind方法是会调用并返回一个IBinder对象,这个对象就是我们B应用所持有的(其实并不是持有的它本身。下面会有讲)。

那么这个IBinder对象就我们自己来实现了,这个IBinder对象具有两种能力,一种是完成特殊任务能力,一种就是跨进程传输能力。那么什么类具有这种能力呢?那就是Binder,看下代码片段。

 public class Binder implement IBinder{
        public void attachInterface(IInterface owner, String descriptor){
                mOwner = owner;
                mDescriptor = descriptor;
        }
        public IInterface queryLocalInterface(String descriptor) {
           if (mDescriptor.equals(descriptor)) {
                    return mOwner;
            }
            return null;
        }
        boolean onTransact(int code, Parcel data, Parcel reply, int flags)//这个方法就是进行处理特殊任务的,后面会用到。
        final class BinderProxy implements IBinder {
        ......//Binder的一个内部类,这个就是《Binder学习心得》里面讲的那个代理类
        }
    }

ok,从上面的代码片段可以看出attachInterface方法以键值对的形势将持有IInterface对象来完成特使任务。queryLocalInterface方法就是用特殊的值即attachInterface方法的键值来获取IInterface对象。特殊能力是讲了,那么跨进程传输能力那就是这个onTransact方法了。现在不详讲,知道在他这做传输就行了。

Ok,完成特殊任务能力和跨进程传输能力都讲了那么我们继承这个Binder以获取这两种能力吧。MyBinder继承Binder并重写onTransact方法。

    class MyBinder extends Binder {
        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            return super.onTransact(code, data, reply, flags);
        }
    }

不过我说过IBinder这个对象要具有完成特殊任务的能力,那么MyService会这么写

 public class MyService extends Service {
        public static String MYBINDER = "this";//这就是那个特殊字符,可以通过他获取IInterface对象。
        @Override
        public IBinder onBind(Intent intent) {
            MyBinder myBinder = new MyBinder();
            myBinder.attachInterface(new MyBusiness(), MYBINDER);//这就是以键值进行存储IInterface引用。开始实现特殊任务的一半了。
            return myBinder;
        }
    }

呃。。这个MyBusiness是什么鬼,这个就是那个特殊任务啊。来个加法吧(*\^__\^*),代码如下。

 class MyBusiness implements IInterface {
        public int add(int a, int b) {
            return a + b;
        }
        /**
         * Retrieve the Binder object associated with this interface.
         * You must use this instead of a plain cast, so that proxy objects
         * can return the correct result.
         */
        @Override
        public IBinder asBinder() {//此方法来自IInterface
            return null;
        }
    }

OK,A应用的这边的整体结构是写完了,虽然没有填充业务,但是我先来看看B应用绑定下这个A应用试试。没绑定咋通信,你说是不?

B应用,点击开始绑定。代码如下

    tv.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                Intent intent = new Intent();
                intent.setAction("www.bjp.ftz.com");
                bindService(intent, conn, BIND_AUTO_CREATE);
                Toast.makeText(MainActivity.this, "已点击", Toast.LENGTH_LONG).show();
            }
        });
    ServiceConnection conn = new ServiceConnection(){
        @Override
        public void onServiceConnected(ComponentName name, IBinder service){
            MainActivity.this.service = service;
            System.out.println(service);
            setData();
        }
        @Override
        public void onServiceDisconnected(ComponentName name){

        }
    };

其上面的流程简述一下,调用bindService这个方法请求ServiceManager这个类进行开启服务,ServiceManager服务就开始匹配找到这个服务并调用这个服务onBind方法获取到了IBinder对象,通过这个IBinder对象得到一个BindProxy这个代理对象(前面有说)。然后通过ServiceConnection的onServiceConnected方法得到一个IBinder对象,我们输出可以以看到这个IBinder对象就是BindProxy对象。(如果有疑问的可以去看我上篇文章《Binder学习心得》

    bjp.ftz.bindertest_b I/Timeline: Timeline: Activity_idle id: android.os.BinderProxy@4296ea80 time:26434129

OK,事实已证明我们得到的就是Binder里面的的那个内部类,那么我来看看BindProxy这个内部类的代码片段

    final class BinderProxy implements IBinder {
        //此方法通过IBinder实现过来
        public IInterface queryLocalInterface(String descriptor) {
            return null;
        }
        /**
         * Perform a generic operation with the object.
         *
         * @param code The action to perform.  This should
         * be a number between
         * @param data Marshalled data to send to the target.Mustnot be null.
         * If you are not sending any data, you must create an empty Parcel
         * that is given here.
         * @param reply Marshalled data to be received from the target.  May be
         * null if you are not interested in the return value.
         * @param flags Additional operation flags.  Either 0 for a normal
         * RPC
         */
         //此方法通过IBinder实现过来
        public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
            return transactNative(code, data, reply, flags);
        }

由上代码可以看出想要通过BinderProxy.queryLocalInterface方法来进行获取IInterface对象是不行的,返回的是一个NULL。那么要怎么才可以呢,然后聚集在了transact方法上面,Perform a generic operation with the object. 意思为执行一个通用的操作对象,不言而喻咯。这个方法需要四个参数第一个参数是表示的动作,即你要调哪一个方法。第二个是要传输的数据,第三个返回的数据,第四个是。。。注释说是0为正常标识所以我们就弄成0咯(*\^__\^*)。ok,开始构造参数。

     @Override
        public void onServiceConnected(ComponentName name, IBinder service){
            Parcel parcel = Parcel.obtain();//传输的数据
            Parcel obtain = Parcel.obtain();//返回的数据
            parcel.writeInterfaceToken("this");//这个this字符串可以随意写,但是在A应用那个地方接收传输数据的时候也要用this字符串。这个方法作用是用来验证要传输的目标接口
            parcel.writeInt(5);//传输a = 5
            parcel.writeInt(5);//传输b = 5
            try {
                boolean transact = service.transact(1, parcel, obtain, 0);//传输过去后,返回一个boolean值表示是否传输成功。
                if(transact){//如果成功,那么我就开始读取返回的结果。
                    obtain.readException();
                    int i = obtain.readInt();
                    System.out.println(i + "=============");
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

当service.transact(1,parcel,obtain,0);执行后,将会调用A应用的onTransact这个方法进行数据读取(是吧,这个方法实现他的传输功能了)。ok,A应用开始开始读取B应用传输过来的数据了。(注意了上文提到的特殊功能的另一半开始实现了)。 代码如下:

     @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            switch (code){//跟B应用的transact方法第一个参数一样。是判断要执行哪个方法和哪个数据。
                case 1: 
                    data.enforceInterface("this");//这个是验证接收数据的目标接口与B应用的parcel.writeInterfaceToke一样
                    int a = data.readInt();//获取a
                    int b = data.readInt();//获取b
                    int add = ((MyBusiness)//返回结果
                    //通过特殊字符串获取myBinder.attachInterface设置进去的IInterface对象,(也就是那个特殊任务)并调用add方法进行加法运算。
                    this.queryLocalInterface("this")).add(5, 5);
                    reply.writeNoException();
                    reply.writeInt(add);//将结果写给B应用
                    return true;//返回true告诉数据指令接收成功
            }
            return super.onTransact(code, data, reply, flags);
        }

OK,整个通信就这样写完了,毋庸置疑,B应用接收到的数据会是10(*^__\^*) 。

如果你整个流程走下来后你会发现,AIDL自动生成的源码就不会是那么难看懂了,这里就不再去跟AIDL做对比了,自己去对比,哈哈~~~~~~~。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值