Android进程间通信之AIDL

一、 摘要

本文介绍Android中的IPC方式之一——AIDL。


二、 关于AIDL

AIDL:Android Interface Definition Language,即Android接口定义语言。

Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。

为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。我们知道4个Android应用程序组件中的3个(Activity、BroadcastReceiver和ContentProvider)都可以进行跨进程访问,另外一个Android应用程序组件Service同样可以。因此,可以将这种可以跨进程访问的服务称为AIDL(Android Interface Definition Language)服务。


三、 编写一个Demo

1. AIDL接口

以Android Studio为例,鼠标在module上点击右键 - new - AIDL - AIDL File,之后,会在该module下自动创建一个名叫aidl的和src同级的文件夹,其中包含一个和src下同包名的aidl文件,这就是我们的服务接口。

我们编写完接口后,需要对module重新build一次,然后会自动生成和该aidl接口同名的java接口文件。关于aidl中接口的规范和约束,建议读者学习这篇文章:你真的理解AIDL中的in,out,inout么?

这个自动生成的同名java文件,请不要修改它,为了便于观察,我将其格式化,然后在注释中讲解:

package com.zengyu.aidldemo;

public interface IDemo extends android.os.IInterface {
    /**
     * 继承Binder,这个很关键,Android中的IPC,底层都是基于Binder实现,
     * 这个静态内部类,实现了外部类的接口,但是由于是抽象类,所以没有真正实现,
     * 因此后面当我们使用这个Stub时,需要自己去实现外部类的接口,也就是aidl文件中的服务接口
     */
    public static abstract class Stub extends android.os.Binder implements com.zengyu.aidldemo.IDemo {
        private static final java.lang.String DESCRIPTOR = "com.zengyu.aidldemo.IDemo";

        /**
         * Stub主要做三件事:
         * 1. 将自身与Binder进行关联;
         * 2. 将方法名以字符串形式进行映射(因此aidl不支持方法的不同入参的重载);
         * 3. 实现每个服务接口包裹化的读写
         * 这些都在自动生成时替我们完成了,我们无需关注具体内容
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * 从Binder到aidl接口的转换
         */
        public static com.zengyu.aidldemo.IDemo asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.zengyu.aidldemo.IDemo))) {
                return ((com.zengyu.aidldemo.IDemo) iin);
            }
            return new com.zengyu.aidldemo.IDemo.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        /**
         * 服务接口方法映射
         */
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_seyHello: {
                    data.enforceInterface(descriptor);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    java.lang.String _result = this.seyHello(_arg0);
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        /**
         * 服务接口包裹化读写实现
         */
        private static class Proxy implements com.zengyu.aidldemo.IDemo {
            private android.os.IBinder mRemote;

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

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public java.lang.String seyHello(java.lang.String from) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(from);
                    mRemote.transact(Stub.TRANSACTION_seyHello, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_seyHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    /**
     * 需要我们之后使用时去实现的服务接口
     */
    public java.lang.String seyHello(java.lang.String from) throws android.os.RemoteException;
}

2. 服务端

我们在Service中实例化一个Stub(两种启动方式的重载方法由读者自己实现,此处不展示):

public class AIDLService extends Service {
    private final IDemo.Stub mBinder = new IDemo.Stub() {
        @Override
        public String seyHello(String from) throws RemoteException {
            return "Hello client, I receive your msg: " + from;
        }
    };

    public AIDLService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        /**
         * Binder是IPC的基础,我们可以在各处见到它
         */
        return mBinder;
    }
}

别忘了在AndroidManifest中注册:

<service
    android:name=".AIDLService"
     <!--表示允许其他应用调用我们这个Service-->
    android:exported="true">
</service>

3. 客户端

客户端需要包含相同的aidl文件,也就是说,在客户端的module中,同样需要创建一个aidl文件,其路径、文件名、内容,应该与使用到的服务端的aidl保持一致。

然后我们在一个Activity中绑定远程的服务端Service:

public class MainActivity extends AppCompatActivity {
    private IDemo iDemo = null;

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            /**
             * 将服务端的Binder实例转换为客户端的aidl接口
             */
            iDemo = IDemo.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            iDemo = null;
        }
    };

    /**
     * 我们需要在Activity创建时绑定Service,通常在onCreate中
     */
    private void bindService() {
        Intent intent = new Intent();
        // 如果我们给Service添加了用于启动的filter:
        intent.setAction("启动的filter");
        // TODO 使用setComponent或者setPackage来指定Service所在的应用
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }

    private void testSending() {
        try {
            iDemo.seyHello("MainActivity");
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

同样,别忘了检查我们的Activity是否在AndroidManifest中注册了。


四、 参考文献

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值