Android中的IPC进程通信方式第三篇

本文系转载文章,阅读原文可获取源码,文章末尾有原文链接

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? = null
override 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:

        android:process=":remote">

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

class AIDLActivity : AppCompatActivity() {

var mServiceConnection: ServiceConnection? = null
var mICommunicationManager: ICommunicationManager? = null
var num: Int = 1
override 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:


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">

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

图片

当我们点击“开启一个服务”按钮之后,再点击“发送消息给服务”按钮,这时候有以下日志打印,注意在如下圈的地方切换到 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,那么客户端的请求会失败。

你可能感兴趣的:(Android中的IPC进程通信方式第三篇)