本文系转载文章,浏览原文可获取源码,文章开端有原文链接

ps:本文的讲的是应用 AIDL 进行过程间通信,demo 是用 Kotlin 语言写的

1、应用 AIDL

AIDL 的全称是 Android Interface Definition Language,也就是 Android 接口定义语言,应用 AIDL 也能够实现跨过程的办法调用,在上一篇文章Android中的IPC过程通信形式第二篇中,咱们用了 Messenger 进行跨过程通信,AIDL 是 Messenger 的底层实现,所以 Messenger实质上也是AIDL,零碎给咱们做了封装从而更不便调用;Messenger 进行跨过程通信时申请队列是同步进行的,无奈并发执行,在有些要求多过程的状况下不实用——这种时候就须要应用 AIDL 了。

应用 AIDL 进行跨过程通信时,并不是所有的数据都反对传输的,而 AIDL 反对以下数据:

1)根本数据类型

2)String 和 CharSequence

3)List 类型:List 中的所有元素必须是 AIDL 反对的类型之一,或者是一个其余 AIDL 生成的接口

4)Map: 只反对 HashMap,外面的每个元素都必须被 AIDL 反对,包含 key和 value

5)Parcelable: 所有实现了 Parcelable 接口的对象

6)AIDL: 所有的AIDL接口自身能够在AIDL文件中应用

上面咱们举个例子:

(1)在我的项目的 main 文件夹下创立一个 aidl 文件,文件名为 ICommunicationManager.aidl

图片

文件 ICommunicationManager.aidl 一开始创立好的代码如下所示:

interface ICommunicationManager {

/** * 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);        

}

下面的 ICommunicationManager.aidl 文件代码是主动生成的,咱们在其根底上增加客户端和服务端通信的办法,比方增加 sendMessage 办法,留神,这里写的办法不是依照 kotlin 的语法来写的,而是依照 Java 的语法来写的;如果 ICommunicationManager 中援用了咱们自定义的其余类,即便其余类和 ICommunicationManager 在同一个包下,也要手动写出导入其余类的语句:

interface ICommunicationManager {

/** * 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);        void sendMessage(in String msg);

}

写完 ICommunicationManager.aidl 这个文件的代码之后对我的项目进行编译,编译完了之后,在 app\build\generated\source\aidl\debug\com\xe\demo\ipcdemo3目录下有一个 ICommunicationManager.java 文件,ICommunicationManager.java 里主动生成的代码咱们先不做剖析,前面再剖析,目录构造如下所示:

图片

(2)新建一个 kotlin 语言的类 AIDLService 并继承于 Service:

class AIDLService: Service() {

var TAG: String = "AIDLService"var mBinder: Binder? = nulloverride fun onBind(intent: Intent?): IBinder {    return mBinder!!}override fun onCreate() {    super.onCreate()    initBinder()}fun initBinder() {    mBinder = ImplCommunicationManager()}inner class ImplCommunicationManager: ICommunicationManager.Stub() {    override fun basicTypes(anInt: Int, aLong: Long, aBoolean: Boolean, aFloat: Float, aDouble: Double, aString: String?) {    }    override fun sendMessage(msg: String?) {        Log.d(TAG,msg)    }}

}

(3)在 AndroidManifest.xml 文件中配置一下 AIDLService:

<service android:name="com.xe.demo.ipcservice.AIDLService"

        android:process=":remote">

</service>

(4)新建一个 Activity,选用语言为 kotlin 语言,类名为 AIDLActivity:

class AIDLActivity : AppCompatActivity() {

var mServiceConnection: ServiceConnection? = nullvar mICommunicationManager: ICommunicationManager? = nullvar num: Int = 1override fun onCreate(savedInstanceState: Bundle?) {    super.onCreate(savedInstanceState)    setContentView(R.layout.activity_aidl)    initServiceConnection()}fun initServiceConnection() {    mServiceConnection = MyServiceConnection();}fun onClick(v: View) {    if (v.id == R.id.btn_1) {       bindService()    } else if (v.id == R.id.btn_2) {        sendMessage()    }}fun sendMessage() {    var msg = "从客户端发送" + num + "条音讯给服务端"    num++    try {        mICommunicationManager!!.sendMessage(msg)    } catch (e: RemoteException) {    }}fun bindService() {    var intent: Intent = Intent(this, AIDLService::class.java)    bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE)}inner class MyServiceConnection: ServiceConnection {    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {        mICommunicationManager = ICommunicationManager.Stub.asInterface(service)    }    override fun onServiceDisconnected(name: ComponentName?) {    }}

}

(5)写一下 AIDLActivity 对应的布局文件 activity_aidl:

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

xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context="com.xe.demo.ipcdemo3.AIDLActivity"><TextView    android:layout_width="match_parent"    android:text="这是AIDL的客户端"    android:layout_height="wrap_content" /><Button    android:id="@+id/btn_1"    android:layout_width="match_parent"    android:onClick="onClick"    android:layout_height="wrap_content"    android:text="开启一个服务"/><Button    android:id="@+id/btn_2"    android:layout_width="match_parent"    android:onClick="onClick"    android:layout_height="wrap_content"    android:text="发送音讯给服务"/>

</LinearLayout>

程序一开始运行的界面如下所示:

图片

当咱们点击“开启一个服务”按钮之后,再点击“发送音讯给服务”按钮,这时候有以下日志打印,留神在如下圈的中央切换到 com.xe.demo.ipcdemo3:remote,因为咱们的服务 AIDLService 指定的过程名就是这个

图片

从日志能够看出,咱们服务 AIDLService 通过 AIDL 接管到了客户端 AIDLActivity 发送过去的信息。

2、AIDL 的源码剖析

首先咱们关上编译过的 ICommunicationManager.java 文件,它是零碎主动生成的

/*

  • This file is auto-generated. DO NOT MODIFY.
  • Original file: D:\androidDemo\IPCDemo3\app\src\main\aidl\com\xe\demo\ipcdemo3\ICommunicationManager.aidl
    */

package com.xe.demo.ipcdemo3;
// Declare any non-default types here with import statements

public interface ICommunicationManager extends android.os.IInterface {

/** * Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements com.xe.demo.ipcdemo3.ICommunicationManager {    private static final java.lang.String DESCRIPTOR = "com.xe.demo.ipcdemo3.ICommunicationManager";    /**     * Construct the stub at attach it to the interface.     */    public Stub() {        this.attachInterface(this, DESCRIPTOR);    }    /**     * Cast an IBinder object into an com.xe.demo.ipcdemo3.ICommunicationManager interface,     * generating a proxy if needed.     */    public static com.xe.demo.ipcdemo3.ICommunicationManager asInterface(android.os.IBinder obj) {        if ((obj == null)) {            return null;        }        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);        if (((iin != null) && (iin instanceof com.xe.demo.ipcdemo3.ICommunicationManager))) {            return ((com.xe.demo.ipcdemo3.ICommunicationManager) iin);        }        return new com.xe.demo.ipcdemo3.ICommunicationManager.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_sendMessage: {                data.enforceInterface(DESCRIPTOR);                java.lang.String _arg0;                _arg0 = data.readString();                this.sendMessage(_arg0);                reply.writeNoException();                return true;            }        }        return super.onTransact(code, data, reply, flags);    }    private static class Proxy implements com.xe.demo.ipcdemo3.ICommunicationManager {        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 void sendMessage(java.lang.String msg) 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.writeString(msg);                mRemote.transact(Stub.TRANSACTION_sendMessage, _data, _reply, 0);                _reply.readException();            } finally {                _reply.recycle();                _data.recycle();            }        }    }    static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);    static final int TRANSACTION_sendMessage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);}/** * 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 void sendMessage(java.lang.String msg) throws android.os.RemoteException;

}

ICommunicationManager 接口继承了 android.os.IInterface 接口,继承 IInterface 接口的接口都能够在 Binder 中传输数据,它申明了 sendMessage 办法,是咱们在 ICommunicationManager.aidl 中申明的办法;同时它用了 id 标识了这个个办法,其实这个 id 是用来标识在 transact 过程中客户端所申请的到底是哪个办法,只是咱们在ICommunicationManager.aidl 文件中只申明了一个办法而已,不然申明的办法数也越多,id 也越多,id 的个数和办法数是相等的;它外部有一个类 Stub,Stub 是一个 Binder 类,当客户端和服务端都位于不在同一个过程时,就会走 transact 办法,这个逻辑由 Stub 的外部代理类 Proxy 来实现,Stub 实现了 IInterface 接口,表明它具备 Server 承诺给客户端的能力。

2、1 Stub 的 asInterface 办法

  public static com.xe.demo.ipcdemo3.ICommunicationManager asInterface(android.os.IBinder obj) {        if ((obj == null)) {            return null;        }        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);        if (((iin != null) && (iin instanceof com.xe.demo.ipcdemo3.ICommunicationManager))) {            return ((com.xe.demo.ipcdemo3.ICommunicationManager) iin);        }        return new com.xe.demo.ipcdemo3.ICommunicationManager.Stub.Proxy(obj);    }

它的用途就是判断客户端和服务器端是否在同一个过程中,如果在同一个过程中就是间接返回本地对象 iin ,否则返回代理对象 new com.xe.demo.ipcdemo3.ICommunicationManager.Stub.Proxy(obj),这示意当不在同一个过程的时候,客户端拿到的其实不是服务端 Binder 自身,而是一个正本。

2、2 Stub 的 asBinder 办法

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

返回以后的 Binder 对象,就拿咱们的 demo 类说就是咱们新建的外部类 ImplCommunicationManager 的对象。

2、3 Proxy 的 sendMessage 办法

        @Override        public void sendMessage(java.lang.String msg) 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.writeString(msg);                mRemote.transact(Stub.TRANSACTION_sendMessage, _data, _reply, 0);                _reply.readException();            } finally {                _reply.recycle();                _data.recycle();            }        }

这个 sendMessage 办法是咱们在 ICommunicationManager.aidl 文件中申明的,通过代理类 Proxy 来实现,在 sendMessage 办法中通过 Parcel 将数据序列化,_data 对象用于装载数据,而后应用 IBinder 类型的 mRemote 对象中的 transact 办法将数据传递给 Binder 的 服务端,同时以后线程挂起,而后服务端的 onTransact 办法会被调用,直到近程过程调用过程返回后,以后线程继续执行,并从 _reply 中取出近程过程调用过程的返回后果,最初返回 _reply 中的数据。

2、4 DESCRIPTOR 标识

private static final java.lang.String DESCRIPTOR = "com.xe.demo.ipcdemo3.ICommunicationManager";

Binder 的惟一标识,用以后 Binder 的类名示意,例如以后 demo 的 Binder 类名 com.xe.demo.ipcdemo3.ICommunicationManager。

2、5 Stub 的 onTransact 办法

    @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_sendMessage: {                data.enforceInterface(DESCRIPTOR);                java.lang.String _arg0;                _arg0 = data.readString();                this.sendMessage(_arg0);                reply.writeNoException();                return true;            }        }        return super.onTransact(code, data, reply, flags);    }

这个办法运行在服务端中的 Binder 线程池中,当客户端发动跨过程办法调用时,零碎底层会把近程办法封装后交由此办法来解决;服务端通过 code 辨认客户端所申请的近程办法(这里的近程办法以本 demo 为例,客户端 AIDLActivity 的 mICommunicationManager!!.sendMessage(msg) 申请),
如果近程办法有参数就会从 data 中取出近程办法所需的参数再执行近程办法;如果近程办法有返回值,就会向 reply 中写入返回值;如果 onTransact 办法返回的是 false,那么客户端的申请会失败。