AIDL概述

AIDL(Android Interface Definition Language),是Android接口定义语言,这种语言定义了一个客户端和服务器通讯接口的一个标准、规范。Google官方AIDL的说明如下:

Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.

意思是说“只有当你允许来自不同应用通过你的service实现进程通讯,并且需要在你的service中处理多线程的情况下才用AIDL,如果你不需要实现不同应用间即时的进程通讯,那么,你应该创建一个接口实现Binder,或者,如果你想实现进程通讯但是不需要处理多线程,那么用一个Messenger实现你的接口,但是,无论如何,你都得先理解本地的服务在你实现AIDL之前“
通过上面的这句话我们就非常清楚了AIDL的作用就是让两个不同的应用间通过Service进行通信(进程通讯IPC),并且远程的Service可以处理多线程。简单来讲就是,两个应用,一个应用对外提供一个远程Service,其他的应用可以并发地访问这个Service,即:C/S模式。

AIDL使用示例

服务端实现

1.通过Android Studio新建一个Module,这个Module作为服务端应用。

2.建立AIDL文件。在src/main目录下新建一个aidl文件夹,然后,在此目录下新建一个IRemoteService.aidl文件。如下:

// IRemoteService.aidlpackage com.lonly.example.aidlservicer;//用到的自定义类型数据类一定要手动导入import com.lonly.example.aidlservicer.Person;// Declare any non-default types here with import statementsinterface IRemoteService {    /**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,            double aDouble, String aString);     /**      * Request the process ID of this service, to do evil things with it      */     int getPid();     /**      * get name by id      */     String getName(int id);     /**     * get Person     */     Person getPerson(int id);     /**     * 添加人员 in:定向tag。     * 其中,      * in 表示数据只能由客户端流向服务端,     * out 表示数据只能由服务端流向客户端,     * inout 则表示数据可在服务端与客户端之间双向流通。     */     void addPerson(in Person person);     List getPersons();}


注意:此处Person类是自定义的,所以,必须加入“import com.lonly.example.aidlservicer.Person;”这语句,即使IRemoteService.aidl和Person.java放在相同的包中。如果不加,有可能在编译时报“Error:Execution failed for task ':android:compileDebugAidl...'”错误,导致编译失败。

在IRemoteService.aidl文件中,我们使用到了自定义的Person类(同样放在src/main/aidl目录下),该类必须实现序列化接口Parcelable。定义如下:

public class Person implements Parcelable{    private String name;    private int age;    public Person(String name, int age) {        this.name = name;        this.age = age;    }    protected Person(Parcel in) {        name = in.readString();        age = in.readInt();    }    public String getName() {        return name;    }    @Override    public String toString() {        return "Person{" +                "name='" + name + '\'' +                ", age=" + age +                '}';    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    /**     * 在想要进行序列号传递的实体类内部一定要声明该常量。常量名只能是CREATOR,类型也必须是     * Parcelable.Creator  T:就是当前对象类型     */    public static final Creator CREATOR = new Creator() {        /***         * 根据序列化的Parcel对象,反序列化为原本的实体对象         * 读出顺序要和writeToParcel的写入顺序相同         */        @Override        public Person createFromParcel(Parcel in) {            return new Person(in.readString(),in.readInt());        }        @Override        public Person[] newArray(int size) {            return new Person[0];        }    };    @Override    public int describeContents() {        return 0;    }    /**     * 将对象写入到Parcel(序列化)     * @param dest:就是对象即将写入的目的对象     * @param flags: 有关对象序列号的方式的标识     * 这里要注意,写入的顺序要和在createFromParcel方法中读出的顺序完全相同。例如这里先写入的为name,     * 那么在createFromParcel就要先读name     */    @Override    public void writeToParcel(Parcel dest, int flags) {        dest.writeString(name);        dest.writeInt(age);    }    /**     * 参数是一个Parcel,用它来存储与传输数据     * @param dest     */    private void readFromParcel(Parcel dest) {        //注意,此处的读值顺序应当是和writeToParcel()方法中一致的        this.name = dest.readString();        this.age = dest.readInt();    }}

借助Android Studio的快捷提示功能,可以很方便的编写出该Person类,只是下面这个方法是我们手动添加的:

 /**     * 参数是一个Parcel,用它来存储与传输数据     * @param dest     */    private void readFromParcel(Parcel dest) {        //注意,此处的读值顺序应当是和writeToParcel()方法中一致的        this.name = dest.readString();        this.age = dest.readInt();    }
为了一直方便,我们把Person.java类也存放到与IRemoteService.aidl文件一致的src/main/aidl目录下。不过,这要修改 build.gradle 文件,在 android{} 中间加上下面的内容: 

sourceSets {    main {        java.srcDirs = ['src/main/java', 'src/main/aidl']    }}

另外,在定义好了Person类之后,我们还需定义一个此类对应的aidl文件,即文件名和类名一样,扩展名为.aidl。如下

// Person.aidlpackage com.lonly.example.aidlservicer;// Declare any non-default types here with import statementsparcelable Person;

注意:parcelable首字母是小写。

3.编译运行项目。编译成功后,会在build目录下自动生成一个IRemoteService.aidl所对应的IRemoteService.java文件。如下:

/* * This file is auto-generated.  DO NOT MODIFY. * Original file: E:\\ASWorkSpace\\AIDLDemo\\aidlservicer\\src\\main\\aidl\\com\\lonly\\example\\aidlservicer\\IRemoteService.aidl */package com.lonly.example.aidlservicer;// Declare any non-default types here with import statementspublic interface IRemoteService extends android.os.IInterface{/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements com.lonly.example.aidlservicer.IRemoteService{private static final java.lang.String DESCRIPTOR = "com.lonly.example.aidlservicer.IRemoteService";/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR);}/** * Cast an IBinder object into an com.lonly.example.aidlservicer.IRemoteService interface, * generating a proxy if needed. */public static com.lonly.example.aidlservicer.IRemoteService asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.lonly.example.aidlservicer.IRemoteService))) {return ((com.lonly.example.aidlservicer.IRemoteService)iin);}return new com.lonly.example.aidlservicer.IRemoteService.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{switch (code){case INTERFACE_TRANSACTION:{reply.writeString(DESCRIPTOR);return true;}case TRANSACTION_basicTypes:{data.enforceInterface(DESCRIPTOR);int _arg0;_arg0 = data.readInt();long _arg1;_arg1 = data.readLong();boolean _arg2;_arg2 = (0!=data.readInt());float _arg3;_arg3 = data.readFloat();double _arg4;_arg4 = data.readDouble();java.lang.String _arg5;_arg5 = data.readString();this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);reply.writeNoException();return true;}case TRANSACTION_getPid:{data.enforceInterface(DESCRIPTOR);int _result = this.getPid();reply.writeNoException();reply.writeInt(_result);return true;}case TRANSACTION_getName:{data.enforceInterface(DESCRIPTOR);int _arg0;_arg0 = data.readInt();java.lang.String _result = this.getName(_arg0);reply.writeNoException();reply.writeString(_result);return true;}case TRANSACTION_getPerson:{data.enforceInterface(DESCRIPTOR);int _arg0;_arg0 = data.readInt();com.lonly.example.aidlservicer.Person _result = this.getPerson(_arg0);reply.writeNoException();if ((_result!=null)) {reply.writeInt(1);_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);}else {reply.writeInt(0);}return true;}case TRANSACTION_addPerson:{data.enforceInterface(DESCRIPTOR);com.lonly.example.aidlservicer.Person _arg0;if ((0!=data.readInt())) {_arg0 = com.lonly.example.aidlservicer.Person.CREATOR.createFromParcel(data);}else {_arg0 = null;}this.addPerson(_arg0);reply.writeNoException();return true;}case TRANSACTION_getPersons:{data.enforceInterface(DESCRIPTOR);java.util.List _result = this.getPersons();reply.writeNoException();reply.writeTypedList(_result);return true;}}return super.onTransact(code, data, reply, flags);}private static class Proxy implements com.lonly.example.aidlservicer.IRemoteService{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;}/**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeInt(anInt);_data.writeLong(aLong);_data.writeInt(((aBoolean)?(1):(0)));_data.writeFloat(aFloat);_data.writeDouble(aDouble);_data.writeString(aString);mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);_reply.readException();}finally {_reply.recycle();_data.recycle();}}@Override public int getPid() 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);mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0);_reply.readException();_result = _reply.readInt();}finally {_reply.recycle();_data.recycle();}return _result;}/**      * get name by id      */@Override public java.lang.String getName(int id) 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.writeInt(id);mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);_reply.readException();_result = _reply.readString();}finally {_reply.recycle();_data.recycle();}return _result;}/**     * get Person     */@Override public com.lonly.example.aidlservicer.Person getPerson(int id) throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();com.lonly.example.aidlservicer.Person _result;try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeInt(id);mRemote.transact(Stub.TRANSACTION_getPerson, _data, _reply, 0);_reply.readException();if ((0!=_reply.readInt())) {_result = com.lonly.example.aidlservicer.Person.CREATOR.createFromParcel(_reply);}else {_result = null;}}finally {_reply.recycle();_data.recycle();}return _result;}/**     * 添加人员 in:定向tag。     * 其中,      * in 表示数据只能由客户端流向服务端,     * out 表示数据只能由服务端流向客户端,     * inout 则表示数据可在服务端与客户端之间双向流通。     */@Override public void addPerson(com.lonly.example.aidlservicer.Person person) throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);if ((person!=null)) {_data.writeInt(1);person.writeToParcel(_data, 0);}else {_data.writeInt(0);}mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);_reply.readException();}finally {_reply.recycle();_data.recycle();}}@Override public java.util.List getPersons() throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();java.util.List _result;try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_getPersons, _data, _reply, 0);_reply.readException();_result = _reply.createTypedArrayList(com.lonly.example.aidlservicer.Person.CREATOR);}finally {_reply.recycle();_data.recycle();}return _result;}}static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);static final int TRANSACTION_getPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);static final int TRANSACTION_getPersons = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);}/**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;public int getPid() throws android.os.RemoteException;/**      * get name by id      */public java.lang.String getName(int id) throws android.os.RemoteException;/**     * get Person     */public com.lonly.example.aidlservicer.Person getPerson(int id) throws android.os.RemoteException;/**     * 添加人员 in:定向tag。     * 其中,      * in 表示数据只能由客户端流向服务端,     * out 表示数据只能由服务端流向客户端,     * inout 则表示数据可在服务端与客户端之间双向流通。     */public void addPerson(com.lonly.example.aidlservicer.Person person) throws android.os.RemoteException;public java.util.List getPersons() throws android.os.RemoteException;}
打开这个AIDL生成的Java接口,我们可以发现,里面有一个内部静态抽象类Stub,这个类继承了Binder并实现了这个接口,所以,我们可以直接使用这个Stub类完成远程服务的搭建。
4.创建RemoteService类,实现该AIDL文件生成的Java接口。在onBind方法中返回实现了AIDL Java接口的那个Binder类(new一个Stub类正好),这个类将作为这个Service代理类。如下:

public class RemoteService extends Service{    private List persons = new ArrayList<>();    /**     * 返回一个RemoteService代理对象IBinder给客户端使用     * @param intent     * @return     */    @Nullable    @Override    public IBinder onBind(Intent intent) {        return mRemoteService;    }    @Override    public void onCreate() {        super.onCreate();        //初始化persons        persons.add(new Person("吕布",20));        persons.add(new Person("关羽",23));        persons.add(new Person("张飞",21));        persons.add(new Person("赵子龙",19));    }    /**     * 实现接口     */    private IRemoteService.Stub mRemoteService = new IRemoteService.Stub() {        @Override        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {            System.out.println("Thread: " + Thread.currentThread().getName());            System.out.println("basicTypes aDouble: " + aDouble +" anInt: " + anInt+" aBoolean " + aBoolean+" aString " + aString);        }        @Override        public int getPid() throws RemoteException {            System.out.println("Thread: " + Thread.currentThread().getName());            System.out.println("RemoteService getPid ");            return android.os.Process.myPid();        }        @Override        public String getName(int id) throws RemoteException {            return persons.get(id).getName();        }        @Override        public Person getPerson(int id) throws RemoteException {            return persons.get(id);        }        @Override        public void addPerson(Person person) throws RemoteException {            persons.add(person);        }        @Override        public List getPersons() throws RemoteException {            return persons;        }    };}

5.在Menifest中声明这个Service。

                                                                  

客户端实现

1.新建一个Moudle,这个Moudle作为客户端应用。
2.将服务端定义的AIDL文件和相关java类移植到客户端中。如果相关的文件都是按上述的步骤存放到src/main/aidl目录,那个直接将aidl文件夹复制过去就可以了。保证包名和服务端的一致。最后,别忘了修改 build.gradle 文件,在 android{} 中间加上下面的内容:

sourceSets {    main {        java.srcDirs = ['src/main/java', 'src/main/aidl']    }}

3.定义MainActivity,调用服务端的方法。但在此之前,我们首先要连接上服务端。如下:

 private ServiceConnection conn = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            // 这里的IBinder对象service是代理对象,所以必须调用下面的方法转换成AIDL接口对象            mRemoteService = IRemoteService.Stub.asInterface(service);            int pid = 0;            try {                pid = mRemoteService.getPid();                int currentPid = android.os.Process.myPid();                System.out.println("currentPID: " + currentPid + "  remotePID: " + pid);                mRemoteService.basicTypes(12, 1223, true, 12.2f, 12.3, "有梦就要去追,加油!");            } catch (RemoteException e) {                e.printStackTrace();            }            System.out.println("bind success! " + mRemoteService.toString());        }        @Override        public void onServiceDisconnected(ComponentName name) {            mRemoteService = null;            System.out.println(mRemoteService.toString() + " disconnected! ");        }    };

然后,通过隐式意图绑定远程服务,建立一个服务连接。如下:

// 连接绑定远程服务        Intent intent = new Intent();        intent.setAction("com.lonly.example.aidl");        intent.setPackage("com.lonly.example.aidlservicer");        isConnSuccess = bindService(intent, conn, Context.BIND_AUTO_CREATE);
注意:此处的intent.setAction("com.lonly.example.aidl");参数必须和服务端Menifrst文件Service中声明的action属性一致。

完整的客户端代码是这样的:

public class MainActivity extends AppCompatActivity {    private IRemoteService mRemoteService;    private TextView mTv_result;    private TextView mAll_result;    private EditText mEditText;    private EditText mEditText1;    private EditText mEditText2;    private boolean isConnSuccess;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mEditText = (EditText) findViewById(R.id.editText);        mEditText1 = (EditText) findViewById(R.id.editText1);        mEditText2 = (EditText) findViewById(R.id.editText2);        mTv_result = (TextView) findViewById(R.id.tv_result);        mAll_result = (TextView) findViewById(R.id.all_result);        // 连接绑定远程服务        Intent intent = new Intent();        intent.setAction("com.lonly.example.aidl");        intent.setPackage("com.lonly.example.aidlservicer");        isConnSuccess = bindService(intent, conn, Context.BIND_AUTO_CREATE);    }    public void search(View view) {        if (isConnSuccess) {            // 连接成功            String keyStr = mEditText.getText().toString();            if (!TextUtils.isEmpty(keyStr)) {                int id = Integer.valueOf(keyStr);                try {                    String name = mRemoteService.getName(id);                    mTv_result.setText(name);                } catch (RemoteException ex) {                    ex.printStackTrace();                }            } else {                Toast.makeText(this, "请输入查询", Toast.LENGTH_SHORT).show();            }        } else {            System.out.println("连接失败!");        }    }    public void searchPerson(View view) {        if (isConnSuccess) {            // 连接成功            String keyStr = mEditText.getText().toString();            if (!TextUtils.isEmpty(keyStr)) {                int id = Integer.valueOf(keyStr);                try {                    Person person = mRemoteService.getPerson(id);                    mTv_result.setText(new StringBuffer("姓名:").append(person.getName()).append("\t年龄:").append(String.valueOf(person.getAge())));                } catch (RemoteException ex) {                    ex.printStackTrace();                }            } else {                Toast.makeText(this, "请输入查询", Toast.LENGTH_SHORT).show();            }        } else {            System.out.println("连接失败!");        }    }    public void add(View view) {        if (isConnSuccess) {            // 连接成功            String nameStr = mEditText1.getText().toString();            String ageStr = mEditText2.getText().toString();            if (!TextUtils.isEmpty(nameStr) && !TextUtils.isEmpty(ageStr) ) {                int age = Integer.valueOf(ageStr);                try {                    //判断是否已有此人                    List list = mRemoteService.getPersons();                    for (Person per:list) {                        if(TextUtils.equals(nameStr,per.getName())){                            Toast.makeText(this, "名册已有此人", Toast.LENGTH_SHORT).show();                            return;                        }                    }                    //添加                    Person person = new Person(nameStr,age);                    mRemoteService.addPerson(person);                    //重新获取                    List persons = mRemoteService.getPersons();                    StringBuffer  sb = new StringBuffer ("");                    for (Person per:persons) {                        sb.append("姓名:")                                .append(per.getName())                                .append("\t年龄:")                                .append(per.getAge())                                .append("\n");                    }                    mAll_result.setText(sb.toString());                } catch (RemoteException ex) {                    ex.printStackTrace();                }            } else {                Toast.makeText(this, "请输入姓名和年龄", Toast.LENGTH_SHORT).show();            }        } else {            System.out.println("连接失败!");        }    }    private ServiceConnection conn = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            // 这里的IBinder对象service是代理对象,所以必须调用下面的方法转换成AIDL接口对象            mRemoteService = IRemoteService.Stub.asInterface(service);            int pid = 0;            try {                pid = mRemoteService.getPid();                int currentPid = android.os.Process.myPid();                System.out.println("currentPID: " + currentPid + "  remotePID: " + pid);                mRemoteService.basicTypes(12, 1223, true, 12.2f, 12.3, "有梦就要去追,加油!");            } catch (RemoteException e) {                e.printStackTrace();            }            System.out.println("bind success! " + mRemoteService.toString());        }        @Override        public void onServiceDisconnected(ComponentName name) {            mRemoteService = null;            System.out.println(mRemoteService.toString() + " disconnected! ");        }    };    @Override    protected void onDestroy() {        super.onDestroy();        unbindService(conn);    }}

activity_main布局文件代码:

<?xml version="1.0" encoding="utf-8"?>        

测试

运行服务端应用,然后在运行客户端应用,测试结果:


源码传送门:https://github.com/legallonly/AIDLDemo

更多相关文章

  1. Android Binder进程间通信-ServiceManager代理对象的获取过程
  2. Android中如何使用Intent在Activity之间传递对象[使用Serializab
  3. Android客户端向服务器端发送数据的流程(1)
  4. RTC搭建android下三层应用程序访问服务器MsSql-客户端
  5. Android仿人人客户端
  6. 转:RTC搭建android下三层应用程序访问服务器MsSql-客户端
  7. 用CSS3生成的一个漂亮的android客户端页面

随机推荐

  1. 工作流引擎的测试容器-功能-使用方法-注
  2. 驰骋工作流程引擎回答湖南朋友的21个问题
  3. Java 流行的工作流引擎
  4. 关于修改工作流引擎退回规则的声明-ccflo
  5. 驰骋工作流引擎——发起前置导航设计
  6. CentOS7.6 安装 QtSDK 出错:You need a C+
  7. 关于驰骋BPM对银行行业的问题答复
  8. 驰骋工作流引擎设计系列15 流程中途结束
  9. IntelliJ IDEA启动画面的秘密:当编程遇到
  10. 驰骋工作流引擎设计系列01 流程元素设计