Android 进程间通信:AIDL

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<PersonInfo> getCREATOR() {
        return CREATOR;
    }

    protected PersonInfo(Parcel in) {
        name = in.readString();
        age = in.readInt();
        sex = in.readString();
        height = in.readFloat();
    }

    public static final Creator<PersonInfo> CREATOR = new Creator<PersonInfo>() {
        @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.aidl
package cn.zzw.aidl;
import cn.zzw.aidl.PersonInfo;
// Declare any non-default types here with import statements
interface 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.aidl
package cn.zzw.aidl;

parcelable PersonInfo;

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

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:

        <service android:name=".service.AidlServerService">
            <intent-filter>
                <action android:name="cn.zzw.PERSON.SERVER"></action>
            </intent-filter>
        </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:

log如下:

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

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

附上以上实例的代码:

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

5. AIDL 的工作原理

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

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

/*
 * 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 statements

public 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类的类结构如下:

下面两个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的最终结果就是让进程间通讯变得简单,自动的完成了参数序列化发送以及解析返回数据的一系列麻烦。

 

猜你喜欢

转载自blog.csdn.net/zzw0221/article/details/92616358