1. 概述

AIDL:Android Interface Definition Language,即Android接口定义语言。在 Android 上,一个进程通常无法访问另一个进程的内存。 尽管如此,进程需要将其对象分解成操作系统能够识别的原语,并将对象编组成跨越边界的对象。 编写执行这一编组操作的代码是一项繁琐的工作,因此 Android 会使用 AIDL 来处理。(摘自Google developer网站)。

只有允许不同应用的客户端用 IPC 方式访问服务,并且想要在服务中处理多线程时,才有必要使用 AIDL。 如果您不需要执行跨越不同应用的并发 IPC,就应该通过实现一个 Binder 创建接口;或者,如果您想执行 IPC,但根本不需要处理多线程,则使用 Messenger 类来实现接口。

2. AIDL 支持的数据类型

a. Java 编程语言中的所有基本数据类型。(如 int、long、char、boolean 等等)

b. String 和 CharSequence

c. List 和Map:List /Map 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。

d. AIDL 生成的接口。

e. 实现Parcelable接口的类

3. AIDL 实现步骤:

服务端:

1. 创建 .aidl 文件

此文件定义带有方法签名的编程接口。

2. 实现接口

Android SDK 工具会生成一个以 .aidl 文件命名的 .java 接口文件。生成的接口包括一个名为 Stub的子类,这个子类是其父接口(例如,YourInterface.Stub)的抽象实现,用于声明 .aidl 文件中的所有方法。

3. 向客户端公开接口

实现 Service 并重写onBind() 以返回 Stub 类的实现。

客户端:

1.在项目中加入 .aidl 文件

2. 实现 ServiceConnection 。

3. 调用 bindService ,以传入 ServiceConnetion。

4. 在 onServiceConnected() 实现中,将收到一个 IBinder 实例(名为 service)。调用 YourInterfaceName.Stub.asInterface((IBinder)service),以将返回的参数转换为 YourInterface 类型。

4. AIDL实例

以下为具体的代码实现:

服务端:

1. 创建要传递的实体类 Person,并实现 Parcelable 接口。

package cn.zzw.aidl;import android.os.Parcel;import android.os.Parcelable;public class PersonInfo implements Parcelable{    private String name;    private int age;    private String sex;    private float height;    public PersonInfo(String name, int age, String sex, float height) {        this.name = name;        this.age = age;        this.sex = sex;        this.height = height;    }    public PersonInfo() {    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public String getSex() {        return sex;    }    public void setSex(String sex) {        this.sex = sex;    }    public float getHeight() {        return height;    }    public void setHeight(float height) {        this.height = height;    }    public static Creator getCREATOR() {        return CREATOR;    }    protected PersonInfo(Parcel in) {        name = in.readString();        age = in.readInt();        sex = in.readString();        height = in.readFloat();    }    public static final Creator CREATOR = new Creator() {        @Override        public PersonInfo createFromParcel(Parcel in) {            return new PersonInfo(in);        }        @Override        public PersonInfo[] newArray(int size) {            return new PersonInfo[size];        }    };    @Override    public int describeContents() {        return 0;    }    @Override    public void writeToParcel(Parcel dest, int flags) {        dest.writeString(name);        dest.writeInt(age);        dest.writeString(sex);        dest.writeFloat(height);    }}

注意:此文件所在的包名要和 .aidl 文件所在的包名一样。

2. 创建 IPerson.aidl 文件

// IPerson.aidlpackage cn.zzw.aidl;import cn.zzw.aidl.PersonInfo;// Declare any non-default types here with import statementsinterface IPerson {    /**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */    String sayHello(in PersonInfo personInfo);    int sumnNum(int num1,int num2);}

注意: 需要import 相关的实体类,否则会报如下错误:

Process 'command 'D:\Android\sdk\build-tools\28.0.3\aidl.exe'' finished with non-zero exit value 1

3. 对于实体类 Person 也必须创建对应的 .aidl 文件

// PersonInfo.aidlpackage cn.zzw.aidl;parcelable PersonInfo;

以上三步创建后,目录如下:

Android 进程间通信:AIDL_第1张图片

4. 创建隐式意图的Service(对于此文件所在的包没有要求),并在onBind方法返回已经实现AIdl 接口的对象。

package cn.zzw.aidlserverdemo.service;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;import cn.zzw.aidl.IPerson;import cn.zzw.aidl.PersonInfo;public class AidlServerService extends Service {    private class MyBinder extends IPerson.Stub{        @Override        public String sayHello(PersonInfo personInfo) throws RemoteException {            if(null!=personInfo)            {                String name = personInfo.getName();                int age = personInfo.getAge();                float height = personInfo.getHeight();                String sex = personInfo.getSex();                return "Hello,My Name is "+name;            }            return null;        }        @Override        public int sumnNum(int num1, int num2) throws RemoteException {            return num1+num2;        }    };    @Override    public IBinder onBind(Intent intent) {        return new MyBinder();    }}

在 AndroidManifest.xml 中注册隐式意图的Service:

                                                        

至此Server端的实现就算完成了,接下来就是客户端如何调用了。

 

客户端:

1. 在客户端中实现服务端中的第 1,2,3 步。

注意:文件所在的包名一定要和服务端一样!!!

2. 绑定远程服务,获取服务对象。

    private void bindToClientService() {        intent = new Intent();        intent.setAction("cn.zzw.PERSON.SERVER");        intent.setPackage("cn.zzw.aidlserverdemo");        bindService(intent,conn,BIND_AUTO_CREATE);    }    ServiceConnection conn =new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            mService= IPerson.Stub.asInterface(service);            Log.d(TAG,"Service Connect Successfully");        }        @Override        public void onServiceDisconnected(ComponentName name) {            Log.d(TAG,"Service Connect Fail");        }    };

3.  调用远程服务中的方法

    public void onClick(View v) {        switch (v.getId()) {            case R.id.mBtn_hello:                if(mService!=null)                {                    String msgInfo = null;                    try {                        msgInfo = mService.sayHello(mInfo);                        Toast.makeText(this,msgInfo,Toast.LENGTH_SHORT).show();                    } catch (RemoteException e) {                        e.printStackTrace();                    }                }else {                    Log.d(TAG,"Service is Null");                    bindToClientService();                }                break;            case R.id.mBtn_calculate:                if(mService!=null)                {                    String msgInfo = null;                    try {                       int sumInfo = mService.sumnNum(50,50);                        Toast.makeText(this,""+sumInfo,Toast.LENGTH_SHORT).show();                    } catch (RemoteException e) {                        e.printStackTrace();                    }                }else {                    Log.d(TAG,"Service is Null");                    bindToClientService();                }                break;        }    }

效果:

先尝试只安装Client,会发现绑定不了Service:

Android 进程间通信:AIDL_第2张图片

log如下:

D/Client: Service is NullD/Client: Service is Null

安装服务端后,再次运行客户端后:

Android 进程间通信:AIDL_第3张图片

附上以上实例的代码:

https://download.csdn.net/download/zzw0221/11248522

 

5. AIDL 的工作原理

当创建AIDL文件并Clean Project 代码后,会生成相应的Java文件:

Android 进程间通信:AIDL_第4张图片

 

先来一段伪代码:类整体结构

/* * This file is auto-generated.  DO NOT MODIFY. * Original file: D:\\AndroidProject\\AIDLServerDemo\\app\\src\\main\\aidl\\cn\\zzw\\aidl\\IPerson.aidl */package cn.zzw.aidl;// Declare any non-default types here with import statementspublic interface IPerson extends android.os.IInterface {    /**     * Local-side IPC implementation stub class.     */    public static abstract class Stub extends android.os.Binder implements cn.zzw.aidl.IPerson //1{      ... }    public java.lang.String sayHello(cn.zzw.aidl.PersonInfo personInfo) throws android.os.RemoteException; //2    public int sumnNum(int num1, int num2) throws android.os.RemoteException; //3}

注释2 和注释3 就是我们在.aidl 中定义的方法。

注释1 是一个静态的抽象类,并且继承了Binder类。Binder是Android中实现IPC方式。AIDL就是利用Binder来实现的。

这里就不对于Binder进行解释,后面会对Binder的源码进行解读,再记录一篇关于Binder的原理。

 

看看Stub这个类:

    public static abstract class Stub extends android.os.Binder implements cn.zzw.aidl.IPerson {        private static final java.lang.String DESCRIPTOR = "cn.zzw.aidl.IPerson";        /**         * Construct the stub at attach it to the interface.         */        public Stub() {            this.attachInterface(this, DESCRIPTOR);        }        /**         * Cast an IBinder object into an cn.zzw.aidl.IPerson interface,         * generating a proxy if needed.         */        public static cn.zzw.aidl.IPerson asInterface(android.os.IBinder obj) {            if ((obj == null)) {                return null;            }            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            if (((iin != null) && (iin instanceof cn.zzw.aidl.IPerson))) {                return ((cn.zzw.aidl.IPerson) iin);            }            return new cn.zzw.aidl.IPerson.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_sayHello: {                    data.enforceInterface(descriptor);                    cn.zzw.aidl.PersonInfo _arg0;                    if ((0 != data.readInt())) {                        _arg0 = cn.zzw.aidl.PersonInfo.CREATOR.createFromParcel(data);                    } else {                        _arg0 = null;                    }                    java.lang.String _result = this.sayHello(_arg0);                    reply.writeNoException();                    reply.writeString(_result);                    return true;                }                case TRANSACTION_sumnNum: {                    data.enforceInterface(descriptor);                    int _arg0;                    _arg0 = data.readInt();                    int _arg1;                    _arg1 = data.readInt();                    int _result = this.sumnNum(_arg0, _arg1);                    reply.writeNoException();                    reply.writeInt(_result);                    return true;                }                default: {                    return super.onTransact(code, data, reply, flags);                }            }        }        private static class Proxy implements cn.zzw.aidl.IPerson {            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 sayHello(cn.zzw.aidl.PersonInfo personInfo) 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);                    if ((personInfo != null)) {                        _data.writeInt(1);                        personInfo.writeToParcel(_data, 0);                    } else {                        _data.writeInt(0);                    }                    mRemote.transact(Stub.TRANSACTION_sayHello, _data, _reply, 0);                    _reply.readException();                    _result = _reply.readString();                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }            @Override            public int sumnNum(int num1, int num2) 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(num1);                    _data.writeInt(num2);                    mRemote.transact(Stub.TRANSACTION_sumnNum, _data, _reply, 0);                    _reply.readException();                    _result = _reply.readInt();                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }        }        static final int TRANSACTION_sayHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);        static final int TRANSACTION_sumnNum = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);    }

Stub类的类结构如下:

Android 进程间通信:AIDL_第5张图片

 

下面两个int常量是用来标识我们在接口中定义的方法的:

static final int TRANSACTION_sayHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_sumnNum = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

DESCRIPTOR 常量是Binder的唯一标识。

private static final java.lang.String DESCRIPTOR = "cn.zzw.aidl.IPerson";

 asInterface 方法非常熟悉,上面客户端才用到的,用于将服务端的Binder对象转换为客户端所需要的接口对象。该过程区分进程,如果进程一样,就返回服务端Stub对象本身,否则就返回封装后的Stub.Proxy对象。

        public static cn.zzw.aidl.IPerson asInterface(android.os.IBinder obj) {            if ((obj == null)) {                return null;            }            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            if (((iin != null) && (iin instanceof cn.zzw.aidl.IPerson))) {                return ((cn.zzw.aidl.IPerson) iin);            }            return new cn.zzw.aidl.IPerson.Stub.Proxy(obj);        }

onTransact  方法是实现 Binder 类后重写的方法。这是运行在服务端的Binder线程中的,当客户端发起远程请求后,在底层的方法就会触发此方法。

5. 总结

AIDL的原理就是Binder,AIDL的最终结果就是让进程间通讯变得简单,自动的完成了参数序列化发送以及解析返回数据的一系列麻烦。

更多相关文章

  1. Android中使用SAX方式解析XML文件
  2. AIDL --- Android中的远程接口(3)
  3. 向eclipse中导入android中的sample样例+gen目录缺少R.java文件
  4. Android Studio的Gradle文件方法说明

随机推荐

  1. 干货链接
  2. android 调试利器之 strace
  3. android获取bluetooth的信号强度(RSSI)
  4. Android 开发基础
  5. Porting WiFi drivers to Android(zz)
  6. [译] Android 架构:Part 4 —— 实践 Clea
  7. 很好的资源学习入口_android
  8. Android-TCPDump for Android(抓TCP数据包
  9. Android近场通信---NFC基础(一)
  10. 使用selector修改TextView中字体的颜色