Android中的跨进程通信AIDL、Binder源码详解

AIDL

AIDL是是Android Interface Definition Language的简称,翻译过来就是安卓进程间通信语言。既然是一门“语言”,就有相应的“语法”。

这个AIDL“语言”严格来说肯定不算一门语言,它只是规定了一些简单的“语法”,开发者遵循这些“语法”,就能比较方便、快捷地完成进程间通信的开发工作,其实说它是一个插件更合适。

从根本上来说,Android进程间通信的核心是Binder,我们完全可以利用Binder来完成进程间通信,但开发起来比较复杂、可读性差。Google为了方便我们开发,就提供了AIDL这种插件,我们只需要遵循一些“语法”规则,就能完成进程间通信了。

AIDL使用简单介绍

第一步,在服务端app中,src/main目录下创建aidl目录,创建com.wk.kupart包,在该包下创建Fun.aidl文件,并声明两个方法:

package com.wk.kupart;

interface Fun {
    int add(in int x, in int y);
    int reduce(in int x, in int y);
}

第二步,clean工程,clean完成后,build/generated/source/aidl/debug目录下就会自动生成com.wk.kupart包,该包下有个Fun.java文件,这就是系统根据上面的Fun.aidl文件自动生成的java文件;

第三步,创建MyService类继承自Service,实现onBind()方法,在该方法中返回Fun.stub对象:

public class MyService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // 返回Fun.Stub对象。从自动生成的Fun.java文件中可以看到,Fun.Stub类实现了IBinder接口
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    // new出Stub实例,并实现接口(就是我们自己定义的两个方法)
    private final Fun.Stub mBinder = new Fun.Stub() {
        @Override
        public int add(int x, int y) throws RemoteException {
            return x + y;
        }

        @Override
        public int reduce(int x, int y) throws RemoteException {
            return x - y;
        }
    };
}

第四步,在客户端app中,重复上面的步骤一和步骤二。注意包名、类名、声明的方法等要跟服务端app中的一模一样;

第五步,客户端app绑定远程Service,在onServiceConnected()方法中就可以拿到远程Service的“句柄/引用”了:

public class NewActivity extends Activity {
    private Fun mFun;

    private ServiceConnection mServiceConn = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 通过asInterface()方法把IBinder实例转换成Fun实例,就可以调用Fun中定义的方法了
            mFun = Fun.Stub.asInterface(service);
            try {
                int x = mFun.add(2, 5);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    };

这样,利用AIDL语法完成的进程间通信就完成了,接下来,我们分析下工作原理。

AIDL源码分析

1、对于跨进程的情况(同进程情况后面会说),下面几点一定要记住:

  • 通过定义aidl文件、clean工程生成class文件之后,客户端、服务端都有了Fun类和各自的Fun对象,客户端调用的是客户端的Fun对象,服务端调用的是服务端的Fun对象,只是它们的方法、成员变量一模一样而已,不要搞混了(android跨进程是无法共享内存的);
  • 客户端绑定service成功后,service的onBind()方法会返回IBinder对象,但是这个对象只存在服务端进程中,无法传递到客户端进程;
  • 客户端绑定成功后,onServiceConnected()方法中的参数IBinder,是客户端new的一个IBinder实例,不是服务端返回的。但是通过这个IBinder,可以把指令发送到远程服务端;

2、分析Fun.java源码前,要搞清楚几点,方便分析:

  • Fun继承自IInterface;
  • Stub是Fun的内部类,继承了Binder类,实现了Fun接口。也就是说,Stub具有这两个类以及IInterface的特性,可以被当成Binder、Fun、IInterface实例使用;
  • Proxy是Stub的内部类,实现Fun接口。也就是说Proxy具有了Fun的特性,可以被当做Fun、IInterface实例使用;

3、下面看源码分析,就以注释形式给出,方便阅读。

public interface Fun extends android.os.IInterface
{
    // Fun的静态内部类,继承自Binder,实现了Fun接口(即实现了我们在Fun.aidl中声明的方法)
    public static abstract class Stub extends android.os.Binder implements com.wk.serviceapp.Fun
    {
        private static final java.lang.String DESCRIPTOR = "com.wk.serviceapp.Fun";

        // 通过int值来标识客户端调的到底是哪个方法
        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_reduce = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

        // Stub的构造方法
        public Stub() {
            // attachInterface()是Binder的方法,作用是把IInterface 和 DESCRIPTOR“键值对”保存起来
            this.attachInterface(this, DESCRIPTOR);
        }

        // 通过IBinder对象,生成Fun对象
        public static com.wk.serviceapp.Fun asInterface(android.os.IBinder obj)
        {
            if ((obj==null)) {
                return null;
            }

            // queryLocalInterface()方法就是通过key(即DESCRIPTOR)获取对应的value(即IInterface),查询到的结果就是在构造方法中保存的“键值对”
            // 对于同进程的Service,在创建Stub的时候就把“键值对”保存起来了,所以这里可以获取到对应的value
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.wk.serviceapp.Fun))) {
                return ((com.wk.serviceapp.Fun)iin);
            }

            // 对于跨进程Service,就会new一个Proxy对象返回(Proxy继承自Fun)
            return new com.wk.serviceapp.Fun.Stub.Proxy(obj);
        }

        // 客户端调用add()方法后,客户端的IBinder会把指令、参数发送到服务端,服务端通知对应的的IBinder,并调用其onTransact()方法
        // 由此可以看出IBinder机制底层主要就是实现了:客户端IBinder发出的消息能准确到达服务端对应的IBinder。有了这个能力,跨进程通信就很容易了
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            // 解析参数、flag,调用对应的方法,跟Proxy一样(不懂的可以看下面Proxy的注释),就不重复了
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_add: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    // 这里的this就是Stub对象,而Stub对象是在Service中创建的,创建的时候实现了add()接口
                    int _result = this.add(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case TRANSACTION_reduce: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = this.reduce(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        // Proxy是Stub的内部类,也实现了Fun接口,所以可以被当作Fun类型返回
        private static class Proxy implements com.wk.serviceapp.Fun
        {
            // 绑定Service成功后,客户端创建的的IBinder对象
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote)
            {
                mRemote = remote;
            }

            // 可以看到,客户端调用add()方法时,就是调用的Proxy中的add()方法
            @Override public int add(int x, int y) throws android.os.RemoteException
            {
                // 远程调用时的参数记录在_data中,执行返回的结果会记录在_reply中
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();

                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    // 将参数x、y记录到_data中
                    _data.writeInt(x);
                    _data.writeInt(y);
                    // 调用mRemote的transact()方法,把“调用的是哪个方法”、“参数”、“存放执行结果的容器”传过去
                    // transact()方法又会回调mRemote的onTransact()方法,实现真正地调用,分析到服务端时再分析
                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                    _reply.readException();
                    // 服务端执行完毕后,把结果放在_reply“容器”中,然后从_reply中取出结果
                    _result = _reply.readInt();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
                // 返回服务端的执行结果
                return _result;
            }

            @Override public int reduce(int x, int y) 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);
                    _data.writeInt(x);
                    _data.writeInt(y);
                    mRemote.transact(Stub.TRANSACTION_reduce, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
    }
    public int add(int x, int y) throws android.os.RemoteException;
    public int reduce(int x, int y) throws android.os.RemoteException;
}


自此,一个完成的客户端调用、中间件传递、服务端执行、通过中间件通知客户端流程就分析完毕了。

自定义进程间通信

我们现在已经知道了Binder进程间通信的原理,那我们完全可以自己定义一套,比AIDL更简单、逻辑更清晰。

客户端绑定Service之后只能得到一个IBinder对象,服务端被绑定成功后也只能返回IBinder对象,所以我们自定义也只能基于Binder。

第一步,服务端的实现:

我们知道接收到客户端指令后会调用Binder的onTransact()方法,所以我们只需要自定义onTransact()方法,把执行结果返回给客户端即可。

public class MyService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // 返回我们自定义的IBinder对象
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    private MyBinder mBinder = new MyBinder();

    // 自定义MBinder,集成自Binder
    private class MyBinder extends Binder
    {
        @Override
        // 重写onTransact()方法,处理客户端的指令
        protected boolean onTransact(int code, Parcel data, Parcel reply,
                                     int flags) throws RemoteException
        {
            switch (code)
            {
                // 如果指令code为0x01,就执行加法。这个指令自己定义就行
                case 0x01:
                {
                    // 这个是token,客户端调用时需要设置token与下面值一样
                    data.enforceInterface("com.wk.serviceapp.myservice");
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    // 执行加法
                    int _result = _arg0 + _arg1;
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                // 如果指令code为0x02,就执行减法
                case 0x02:
                {
                    data.enforceInterface("com.wk.serviceapp.myservice");
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    // 执行减法
                    int _result = _arg0 - _arg1;
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

    }
}

在manifest中注册与AIDL一致,就不说了。


第二步,客户端实现:

客户端要做的更简单,就是调用IBinder的Transact()方法通知服务端就可以了。

public class NewActivity extends Activity {

    private ServiceConnection mServiceConn = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            int _result;
            try {
                // 设置token
                _data.writeInterfaceToken("com.wk.serviceapp.myservice");
                // 设置add()方法中的参数1、参数2
                _data.writeInt(2);
                _data.writeInt(5);
                // 调用transact()方法
                service.transact(0x01, _data, _reply, 0);
                _reply.readException();
                // 获取结果
                _result = _reply.readInt();
                Log.i("wk", "result:" + _result);
            } catch (RemoteException e) {
                e.printStackTrace();
            } finally {
                _reply.recycle();
                _data.recycle();
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_new);

        Intent intent = new Intent();
        intent.setAction("com.wk.serviceapp.add");
        intent.setPackage("com.wk.serviceapp");
        bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);
    }
}

就这么简单,一个基于Binder的自定义的进程间通信就完成了,没有aidl文件,没有自动生成的java文件,也不需要注意aidl繁琐的规则。

注意一点,可以看到,客户端调用服务端的方法后,就是同步等待服务端的执行结果,所以不能执行耗时操作。如果要执行耗时操作,就把transact()方法的调用放在子线程中。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值