猿创征文 | Android AIDL 学习笔记——学以致用

Android AIDL

AIDL(Android Interface Definition Language)可以用它定义客户端与服务端均认可的编程接口,以便客户端和服务端进行IPC(Inter-Process Communication 进程间通信)。

在 Android 中,一个进程通常无法访问另一个进程的内存。因此,为进行通信,进程需将其对象分解成可供操作系统理解的原语,并将其编组为可供开发者操作的对象。编写执行该编组操作的代码较为繁琐,因此 Android 会使用 AIDL 来处理此问题,简化相关实现。

跨进程通信

**只有在需要不同应用的客户端通过 IPC 方式访问服务,并且希望在服务中进行多线程处理时,才有必要使用 AIDL。**如果无需跨不同应用执行并发 IPC,则应通过实现 Binder来创建接口;或者,如果想执行 IPC,但不需要处理多线程,可以使用 Messenger来实现接口。

Android系统中大量使用了Binder机制实现IPC,Binder实际实现有些复杂,与直接使用Binder对比,AIDL更加方便。AIDL原理实际也是实现Binder机制。

**AIDL 接口的调用是直接函数调用。不需要对发生调用的线程做任何假设。实际情况的差异取决于调用是来自本地进程中的线程,还是远程进程中的线程。**具体而言:

  • **来自本地进程的调用在发起调用的同一线程内执行。**如果该线程是主界面线程,则其将继续在 AIDL 接口中执行。如果该线程是其他线程,则其便是在服务中执行代码的线程。因此,只有在本地线程访问服务时,才能完全控制哪些线程在服务中执行(但若出现此情况,其实根本无需使用 AIDL,而应通过实现 Binder 类来创建接口)。
  • 远程进程的调用分派自线程池,且系统会在应用的进程内部维护该线程池。须为来自未知线程,且多次调用同时发生的传入调用做好准备。换言之,AIDL 接口的实现必须基于完全的线程安全。如果调用来自同一远程对象上的某个线程,则该调用将依次抵达接收器端。
  • oneway 关键字用于修改远程调用的行为。使用此关键字后,远程调用不会屏蔽,而只是发送事务数据并立即返回。最终接收该数据时,接口的实现会将其视为来自 Binder 线程池的常规调用(普通的远程调用)。如果 oneway 用于本地调用,则不会有任何影响,且调用仍为同步调用

AIDL文件

AIDL在以 .aidl 文件中使用 Java 编程语言的语法定义接口。

然后将其保存至应用的源代码内,这类应用会托管服务或与服务进行绑定。

在构建包含 .aidl 文件的应用时,Android SDK 工具会生成基于该 .aidl 文件的 IBinder 接口,并将其保存到项目的 gen/ 目录中。服务端必须视情况实现 IBinder 接口。然后,客户端应用便可绑定到该服务端,并调用 IBinder 中的方法来执行 IPC。

AIDL语法

AIDL 使用一种简单语法,可以通过一个或多个方法(可接收参数和返回值)来声明接口。参数和返回值可为任意类型,甚至是 AIDL 生成的其他接口。

数据类型

AIDL支持的数据类型如下

  • Java语言中支持的基本数据类型:

    • byte(位)
    • int(整数)
    • long(长整数)
    • float(单精度)
    • double(双精度)
    • char(字符)
    • boolean(布尔值)

    不支持short(短整数)

  • String(字符串)

  • CharSequence(字符队列)

  • List(链表)
    List 中的所有元素必须AIDL支持的数据类型,或者声明由ADIDL生成的其他接口或 Parcelable 类型。

    这里虽然可以使用List但另一方实际接收的具体类始终是 ArrayList

  • Map(集合)

    Map 中的所有元素必须是AIDL支持的数据类型,或者声明由AIDL生成的其他接口或 Parcelable 类型。不支持泛型 Map

    这里虽然可以使用map但另一方实际接收的具体类始终是 HashMap

  • parcelable声明的java类
  • 以上支持类型的数组
void callbackBasicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
                        double aDouble, int aShort/*不支持short,用int代替*/, char aChar, byte aByte);

void callbackObjectTypes(String aString, CharSequence aCharSequence,in List aList,in Map aMap);

void callbackParcelableTypes(in TestAidlJavaBean javaBean);

关键字

  • in 方向标记
    输入类型,调用端到接收端。基本数据类型(bytecharintlongfloatdoubleboolean)和StringCharSequence只能使用in,在文件中可以不显示指定。
  • out 方向标记
    输出类型,接收端到输入端
  • inout 方向标记
    双向支持
  • oneway
    • oneway可以用来修饰在interface之前,这样会造成interface内所有的方法都隐式地带上oneway
    • oneway也可以修饰在interface里的各个方法之前。
    • 被oneway修饰了的方法不可以有返回值,也不可以有带outinout的参数。
boolean setForResult(in String message);
oneway void setOneway(in String message);

void setIn(in int[] arr);
void setOut(out int[] arr);
void setInout(inout int[] arr);

除了基本数据类型(bytecharintlongfloatdoubleboolean)和StringCharSequence外,所有参数都需要显示指定参数的方向标记inoutinout中的一种

引用

即使在与接口相同的包内定义AIDL数据类型中未列出的附加类型,必须为其各自加入一条 import 语句。

import com.android.aidl.TestAidlJavaBean;

指定方法ID

AIDL中方法使用transact方法分配执行的,我们可以在AIDL文件中,指定方法的ID,比如void method() = 10;

void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
                double aDouble, int aShort, char aChar, byte aByte) = 1;

void objectTypes(String aString, CharSequence aCharSequence, in List aList,in Map aMap) = 2;

void parcelableTypes(in TestAidlJavaBean javaBean) = 3;

在一个文件中,只能所有方法都指定 ID或者都不指定。

总结

定义服务接口时需要注意:

  • 方法可带零个或多个参数,返回值或空值。

  • 所有非原语参数均需要指示数据走向的方向标记。这类标记可以是inoutinout,原语默认为 in,不能是其他方向。

    **注意:**应将方向限定为真正需要的方向,因为编组参数的开销较大。

  • 生成的 IBinder 接口内包含 .aidl 文件中的所有代码注释(import 和 package 语句之前的注释除外)。

  • 可以在 AIDL 接口中定义 String 常量和 int 字符串常量。例如:const int VERSION = 1;

  • 方法调用由 transact() 代码 分派,该代码通常基于接口中的方法索引。由于这会增加版本控制的难度,因此可以向方法手动配置事务代码:void method() = 10;

  • 可以使用 @nullable 注释可空参数或返回类型。

实现接口

当构建应用时,Android SDK 工具会生成以 .aidl 文件命名的 .java 接口文件。生成的接口包含一个名为 Stub 的子类(例如,YourInterface.Stub),该子类是其父接口的抽象实现,并且会声明 .aidl 文件中的所有方法。

我们需要继承Stub子类来实现功能。

AIDL文件

package com.android.aidl;

import com.android.aidl.ITestAidlCallback;
import com.android.aidl.TestAidlJavaBean;

interface ITestAidlInterface {

    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, int aShort, char aChar, byte aByte) = 1;

    void objectTypes(String aString, CharSequence aCharSequence, in List aList,in Map aMap) = 2;

    void parcelableTypes(in TestAidlJavaBean javaBean) = 3;
    void setBean(in TestAidlJavaBean javaBean) = 5;

    boolean setForResult(String message) = 8;
    oneway void setOneway(String message) = 9;
    void setCallback(in ITestAidlCallback callback) = 10;

    void setIn(in int[] arr) = 11;
    void setOut(out int[] arr) = 12;
    void setInout(inout int[] arr) = 13;
}

实现ITestAidlInterface我们需要一个类继承ITestAidlInterface.Stub

class TestAidlBinder extends ITestAidlInterface.Stub {
    @Override
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, int aShort, char aChar, byte aByte) throws RemoteException {
    }

    @Override
    public void objectTypes(String aString, CharSequence aCharSequence, List aList, Map aMap) throws RemoteException {

    }

    @Override
    public void parcelableTypes(TestAidlJavaBean javaBean) throws RemoteException {
    }

    @Override
    public boolean setForResult(String message) throws RemoteException {
        return false;
    }

    @Override
    public void setOneway(String message) throws RemoteException {
    }

    @Override
    public void setCallback(ITestAidlCallback callback) throws RemoteException {

    }

    @Override
    public void setBean(TestAidlJavaBean javaBean) throws RemoteException {
    }

    @Override
    public void setIn(int[] arr) throws RemoteException {

    }

    @Override
    public void setOut(int[] arr) throws RemoteException {
    }

    @Override
    public void setInout(int[] arr) throws RemoteException {
    }

    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        return super.onTransact(code, data, reply, flags);
    }
}

在实现 AIDL 接口时,应注意遵守以下规则:

  • 由于无法保证在主线程上执行传入调用,因此一开始便需做好多线程处理的准备,并对服务进行适当构建,使其达到线程安全的标准。
  • 默认情况下,RPC 调用是同步调用。如果知道服务完成请求的时间不止几毫秒,则不应从 Activity 的主线程调用该服务,因为这可能会使应用挂起(Android 可能会显示“Application is Not Responding”对话框)— 通常,应从客户端内的单独线程调用服务。
  • 引发的任何异常都不会回传给调用方。

获取AIDL对象

获取AIDL的对象一般通过bindService方法,在客户端 onServiceConnected() 回调方法的参数中获取。关键函数为StubasInterface()方法。

private ITestAidlInterface mBinder;
Intent intent = new Intent(this, TestAidlService.class);
boolean result = bindService(intent, new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        Log.d(TAG, "onServiceConnected: ");

        mBinder = ITestAidlInterface.Stub.asInterface(iBinder);
        Log.d(TAG, "客户端 onServiceConnected: binder " + mBinder);         
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        Log.d(TAG, "onServiceDisconnected: ");
   
}, BIND_AUTO_CREATE);

捕获异常

获取到AIDL对象后,可以用AIDL中定义的接口进行IPC。在任何调用处,应该捕获RemoteException

try {
    Log.d(TAG, "客户端 test basic: ");
    mBinder.basicTypes(Integer.MAX_VALUE, Long.MAX_VALUE, Boolean.TRUE, Float.MAX_VALUE,
                       Double.MAX_VALUE, Short.MAX_VALUE, Character.MAX_VALUE, Byte.MAX_VALUE);
    mBinder.objectTypes("String","CharSequence".subSequence(0,4),
                        new LinkedList(),new ArrayMap());
    mBinder.parcelableTypes(new TestAidlJavaBean());
} catch (RemoteException e) {
    e.printStackTrace();
}

创建Parcelable对象

可以通过 IPC 接口,将某个类从一个进程发送至另一个进程。不过,必须确保 IPC 通道的另一端可使用该类的代码,并且该类必须支持 Parcelable 接口。支持 Parcelable 接口很重要,因为 Android 系统能通过该接口将对象分解成可编组至各进程的原语。

如要创建支持 Parcelable 协议的类,必须执行以下操作:

  1. 让您的类实现 Parcelable 接口。
  2. 实现 writeToParcel,它会获取对象的当前状态并将其写入 Parcel
  3. 为您的类添加 CREATOR 静态字段,该字段是实现 Parcelable.Creator 接口的对象。
  4. 最后,创建声明 Parcelable 类的.aidl
  • 创建实现Parcelable的类

    package com.android.aidl;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    
    public class TestAidlJavaBean implements Parcelable {
    
        private int anInt;
        private long aLong;
        private String aString;
    
        public TestAidlJavaBean() {
    
        }
    
        protected TestAidlJavaBean(Parcel in) {
        }
    
    
        public static final Creator<TestAidlJavaBean> CREATOR = new Creator<TestAidlJavaBean>() {
            @Override
            public TestAidlJavaBean createFromParcel(Parcel in) {
                TestAidlJavaBean bean = new TestAidlJavaBean();
                bean.anInt = in.readInt();
                bean.aLong = in.readLong();
                bean.aString = in.readString();
                return bean;
            }
    
            @Override
            public TestAidlJavaBean[] newArray(int size) {
                return new TestAidlJavaBean[size];
            }
        };
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel parcel, int i) {
            parcel.writeInt(anInt);
            parcel.writeLong(aLong);
            parcel.writeString(aString);
        }
    
        public TestAidlJavaBean readFromParcel(Parcel parcel) {
            TestAidlJavaBean bean = new TestAidlJavaBean();
            bean.anInt = parcel.readInt();
            bean.aLong = parcel.readLong();
            bean.aString = parcel.readString();
            return bean;
        }
    }
    

    这里需要注意的是,如果数据的传输方向包含in, 则必须要实现writeToParcel接口,如果数据的传输方向包含out则必须要实现readFromParcel接口。

  • 创建声明parcelable的AIDL文件

    package com.android.aidl;
    
    parcelable TestAidlJavaBean;
    

带Bundle参数的方法

如果 AIDL 接口包含接收Bundle作为参数(包含 Parcelable 类型)的方法,则在尝试从Bundle读取之前,请务必通过调用 Bundle.setClassLoader(ClassLoader) 设置Bundle的类加载器。否则,即使在应用中正确定义 Parcelable 类型,也会遇到 ClassNotFoundException

例如.aidl 文件:

package com.example.android;

/** Example service interface */
interface IRectInsideBundle {
    /** Rect parcelable is stored in the bundle with key "rect" */
    void saveRect(in Bundle bundle);
}

java文件。

private final IRectInsideBundle.Stub binder = new IRectInsideBundle.Stub() {
    public void saveRect(Bundle bundle){
        bundle.setClassLoader(getClass().getClassLoader());
        Rect rect = bundle.getParcelable("rect");
        process(rect); // Do more with the parcelable.
    }
};

在读取 Rect 之前,ClassLoader 已在 Bundle 中完成显式设置

IPC调用

这里举个完整调用的例子,主要文件如下

文件描述
ITestAidlCallback.aidl定义从服务调用到客户端的接口
ITestAidlInterface.aidl定义从客户端调用到服务端的接口
TestAidlJavaBean.aidl声明parcelable数据
TestAidlService.java服务端代码
TestAidlJavaBean.java数据类,实现Parcelable接口
TestAidlActivity.java客户端代码(部分)
  • ITestAidlCallback

    // ITestAidlCallback.aidl
    package com.android.aidl;
    
    // Declare any non-default types here with import statements
    import com.android.aidl.TestAidlJavaBean;
    
    interface ITestAidlCallback {
        void callbackBasicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
                double aDouble, int aShort/*不支持short,用int代替*/, char aChar, byte aByte);
    
        void callbackObjectTypes(String aString, CharSequence aCharSequence,in List aList,in Map aMap);
    
        void callbackParcelableTypes(in TestAidlJavaBean javaBean);
        void callbackSetBean(inout TestAidlJavaBean javaBean);
    
        boolean callbackSetForResult(String message);
        oneway void callbackSetOneway(String message);
        void callbackSetCallback(in ITestAidlCallback callback);
    
        void callbackIn(in int[] arr);
        void callbackOut(out int[] arr);
        void callbackInout(inout int[] arr);
    }
    
  • ITestAidlInterface

    // ITestAidlInterface.aidl
    package com.android.aidl;
    
    // Declare any non-default types here with import statements
    import com.android.aidl.ITestAidlCallback;
    import com.android.aidl.TestAidlJavaBean;
    
    interface ITestAidlInterface {
    
        void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
                double aDouble, int aShort, char aChar, byte aByte) = 1;
    
        void objectTypes(String aString, CharSequence aCharSequence, in List aList,in Map aMap) = 2;
    
        void parcelableTypes(in TestAidlJavaBean javaBean) = 3;
        void setBean(in TestAidlJavaBean javaBean) = 5;
    
        boolean setForResult(String message) = 8;
        oneway void setOneway(String message) = 9;
        void setCallback(in ITestAidlCallback callback) = 10;
    
        void setIn(in int[] arr) = 11;
        void setOut(out int[] arr) = 12;
        void setInout(inout int[] arr) = 13;
    }
    
  • TestAidlJavaBean

    // TestAidlJavaBean.aidl
    package com.android.aidl;
    
    parcelable TestAidlJavaBean;
    
  • TestAidlService

    package com.android.aidl;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.os.Parcel;
    import android.os.RemoteException;
    import android.util.ArrayMap;
    import android.util.Log;
    
    import androidx.annotation.Nullable;
    
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Map;
    
    public class TestAidlService extends Service {
        public final String TAG = this.getClass().getSimpleName();
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
    
            TestAidlBinder aidlBinder = new TestAidlBinder();
            Log.d(TAG, "onBind: " + aidlBinder);
            return aidlBinder;
        }
    
    
        class TestAidlBinder extends ITestAidlInterface.Stub {
    
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, int aShort, char aChar, byte aByte) throws RemoteException {
                Log.d(TAG, "服务端 basicTypes: int " + anInt);
                Log.d(TAG, "服务端 basicTypes: long " + aLong);
                Log.d(TAG, "服务端 basicTypes: boolean " + aBoolean);
                Log.d(TAG, "服务端 basicTypes: float " + aFloat);
                Log.d(TAG, "服务端 basicTypes: double " + aDouble);
                Log.d(TAG, "服务端 basicTypes: short " + aShort);
                Log.d(TAG, "服务端 basicTypes: char " + aChar);
                Log.d(TAG, "服务端 basicTypes: byte " + aByte);
            }
    
    
            @Override
            public void objectTypes(String aString, CharSequence aCharSequence, List aList, Map aMap) throws RemoteException {
                Log.d(TAG, "服务端 objectTypes: String " + aString);
                Log.d(TAG, "服务端 objectTypes: CharSequence " + aCharSequence);
                Log.d(TAG, "服务端 objectTypes: List " + aList.getClass() + " " + aList);
                Log.d(TAG, "服务端 objectTypes: Map " + aMap.getClass() + " " + aMap);
            }
    
            @Override
            public void parcelableTypes(TestAidlJavaBean javaBean) throws RemoteException {
                Log.d(TAG, "服务端 parcelableTypes: TestAidlJavaBean " + javaBean);
            }
    
    
    
            @Override
            public boolean setForResult(String message) throws RemoteException {
                Log.d(TAG, "服务端 setForResult: " + message);
                message.replace("m","M");
                Log.d(TAG, "服务端 setForResult set M value: " + message);
    
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return false;
            }
    
            @Override
            public void setOneway(String message) throws RemoteException {
                Log.d(TAG, "服务端 setOneway: " + message);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            @Override
            public void setCallback(ITestAidlCallback callback) throws RemoteException {
                Log.d(TAG, "服务端 setCallback: " + callback);
                TestAidlJavaBean bean = new TestAidlJavaBean();
    
                callback.callbackBasicTypes(Integer.MAX_VALUE, Long.MAX_VALUE, Boolean.TRUE, Float.MAX_VALUE,
                        Double.MAX_VALUE, Short.MAX_VALUE, Character.MAX_VALUE, Byte.MAX_VALUE);
                callback.callbackObjectTypes("String","CharSequence".subSequence(0,4),
                        new LinkedList(),new ArrayMap());
                callback.callbackParcelableTypes(new TestAidlJavaBean());
    
    
                int[] array = new int[]{0,0};
                array[0] = 1;
    
                Log.d(TAG, "服务端 test in before: " +  array[0]);
                callback.callbackIn(array);
                Log.d(TAG, "服务端 test in after: " +  array[0]);
    
                Log.d(TAG, "服务端 test out before: " +  array[0]);
                callback.callbackOut(array);
                Log.d(TAG, "服务端 test out after: " +  array[0]);
    
                Log.d(TAG, "服务端 test inout before: " +  array[0]);
                callback.callbackInout(array);
                Log.d(TAG, "服务端 test inout after: " +  array[0]);
    
            }
    
            @Override
            public void setBean(TestAidlJavaBean javaBean) throws RemoteException {
                Log.d(TAG, "服务端 setBean: " + javaBean);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            @Override
            public void setIn(int[] arr) throws RemoteException {
                Log.d(TAG, "服务端 setIn: " + arr[0]);
                arr[0] = 3;
                Log.d(TAG, "服务端 setIn 3 value: " + arr[0]);
            }
    
            @Override
            public void setOut(int[] arr) throws RemoteException {
                Log.d(TAG, "服务端 setOut: " + arr[0]);
                arr[0] = 3;
                Log.d(TAG, "服务端 setOut 3 value: " + arr[0]);
            }
    
            @Override
            public void setInout(int[] arr) throws RemoteException {
                Log.d(TAG, "服务端 setInout: " + arr[0]);
                arr[0] = 3;
                Log.d(TAG, "服务端 setInout 3 value: " + arr[0]);
            }
    
            @Override
            public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    //            Log.d(TAG, "服务端 onTransact: code " + code);
    //            Log.d(TAG, "服务端 onTransact: data " + data);
    //            Log.d(TAG, "服务端 onTransact: reply " + reply);
    //            Log.d(TAG, "服务端 onTransact: flags " + flags);
                return super.onTransact(code, data, reply, flags);
            }
    
        }
    }
    
    
  • TestAidlJavaBean

    package com.android.aidl;
    
    import android.os.Parcel;
    import android.os.Parcelable;
    
    public class TestAidlJavaBean implements Parcelable {
    
        private int anInt;
        private long aLong;
        private String aString;
    
        public TestAidlJavaBean() {
    
        }
    
        protected TestAidlJavaBean(Parcel in) {
        }
    
    
        public static final Creator<TestAidlJavaBean> CREATOR = new Creator<TestAidlJavaBean>() {
            @Override
            public TestAidlJavaBean createFromParcel(Parcel in) {
                TestAidlJavaBean bean = new TestAidlJavaBean();
                bean.anInt = in.readInt();
                bean.aLong = in.readLong();
                bean.aString = in.readString();
                return bean;
            }
    
            @Override
            public TestAidlJavaBean[] newArray(int size) {
                return new TestAidlJavaBean[size];
            }
        };
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel parcel, int i) {
            parcel.writeInt(anInt);
            parcel.writeLong(aLong);
            parcel.writeString(aString);
        }
    
        public TestAidlJavaBean readFromParcel(Parcel parcel) {
            TestAidlJavaBean bean = new TestAidlJavaBean();
            bean.anInt = parcel.readInt();
            bean.aLong = parcel.readLong();
            bean.aString = parcel.readString();
            return bean;
        }
    }
    
  • TestAidlActivity

    package com.android.aidl;
    
    import android.content.ComponentName;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.Bundle;
    import android.os.IBinder;
    import android.os.RemoteException;
    import android.os.SystemClock;
    import android.util.ArrayMap;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    
    import androidx.annotation.Nullable;
    import androidx.appcompat.app.AppCompatActivity;
    
    import com.android.R;
    
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Map;
    
    public class TestAidlActivity extends AppCompatActivity implements View.OnClickListener {
        public final String TAG = this.getClass().getSimpleName();
    
        private Button mBtn1;
        private Button mBtn2;
        private Button mBtn3;
        private Button mBtn4;
    
        private ITestAidlInterface mBinder;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.aidl_test_binder);
            mBtn1 = findViewById(R.id.btn1);
            mBtn2 = findViewById(R.id.btn2);
            mBtn3 = findViewById(R.id.btn3);
            mBtn4 = findViewById(R.id.btn4); 
    
    
            mBtn1.setOnClickListener(this);
            mBtn2.setOnClickListener(this);
            mBtn3.setOnClickListener(this);
            mBtn4.setOnClickListener(this);
    
            Intent intent = new Intent(this, TestAidlService.class);
            boolean result = bindService(intent, new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                    Log.d(TAG, "onServiceConnected: ");
    
                    mBinder = ITestAidlInterface.Stub.asInterface(iBinder);
                    Log.d(TAG, "客户端 onServiceConnected: binder " + mBinder);
    
    
                    try {
                        Log.d(TAG, "onServiceConnected: " + iBinder.getInterfaceDescriptor());
                        iBinder.linkToDeath(new IBinder.DeathRecipient() {
                            @Override
                            public void binderDied() {
                                Log.d(TAG, "客户端 binderDied: ");
                            }
                        }, 0);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
    
                @Override
                public void onServiceDisconnected(ComponentName componentName) {
                    Log.d(TAG, "onServiceDisconnected: ");
                }
            }, BIND_AUTO_CREATE);
            Log.d(TAG, "onCreate: bind result " + result);
        }
    
    
        @Override
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.btn1:
                    try {
                        Log.d(TAG, "客户端 test basic: ");
                        mBinder.basicTypes(Integer.MAX_VALUE, Long.MAX_VALUE, Boolean.TRUE, Float.MAX_VALUE,
                                Double.MAX_VALUE, Short.MAX_VALUE, Character.MAX_VALUE, Byte.MAX_VALUE);
                        mBinder.objectTypes("String", "CharSequence".subSequence(0, 4),
                                new LinkedList(), new ArrayMap());
                        mBinder.parcelableTypes(new TestAidlJavaBean());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                case R.id.btn2:
                    try {
                        long startTime = SystemClock.elapsedRealtimeNanos();
                        mBinder.setOneway("setOneway");
                        Log.d(TAG, "客户端 test oneway time: " + (SystemClock.elapsedRealtimeNanos() - startTime) + "ns");
    
                        startTime = SystemClock.elapsedRealtimeNanos();
                        String s = "message";
                        boolean result = mBinder.setForResult(s);
                        Log.d(TAG, "onClick: " + s);
                        Log.d(TAG, "客户端 test result " + result + " time: " + (SystemClock.elapsedRealtimeNanos() - startTime) + "ns");
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
    
                    break;
                case R.id.btn3:
                    try {
                        int[] array = new int[]{0, 0};
                        array[0] = 1;
    
                        Log.d(TAG, "客户端 test in before: " + array[0]);
                        mBinder.setIn(array);
                        Log.d(TAG, "客户端 test in after: " + array[0]);
    
                        Log.d(TAG, "客户端 test out before: " + array[0]);
                        mBinder.setOut(array);
                        Log.d(TAG, "客户端 test out after: " + array[0]);
    
                        Log.d(TAG, "客户端 test inout before: " + array[0]);
                        mBinder.setInout(array);
                        Log.d(TAG, "客户端 test inout after: " + array[0]);
    
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                case R.id.btn4:
                    try {
                        mBinder.setCallback(new ITestAidlCallback.Stub() {
                            @Override
                            public void callbackBasicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, int aShort, char aChar, byte aByte) throws RemoteException {
                                Log.d(TAG, "客户端 callbackBasicTypes: ");
                            }
    
                            @Override
                            public void callbackObjectTypes(String aString, CharSequence aCharSequence, List aList, Map aMap) throws RemoteException {
                                Log.d(TAG, "客户端 callbackObjectTypes: ");
                            }
    
                            @Override
                            public void callbackParcelableTypes(TestAidlJavaBean javaBean) throws RemoteException {
                                Log.d(TAG, "客户端 callbackParcelableTypes: " + javaBean);
                            }
    
    
                            @Override
                            public boolean callbackSetForResult(String message) throws RemoteException {
                                Log.d(TAG, "客户端 callbackSetForResult: ");
                                return false;
                            }
    
                            @Override
                            public void callbackSetOneway(String message) throws RemoteException {
                                Log.d(TAG, "客户端 callbackSetOneway: ");
                            }
    
                            @Override
                            public void callbackSetCallback(ITestAidlCallback callback) throws RemoteException {
                                Log.d(TAG, "客户端 callbackSetCallback: " + callback);
    
                            }
    
                            @Override
                            public void callbackSetBean(TestAidlJavaBean javaBean) throws RemoteException {
                                Log.d(TAG, "客户端 callbackSetBean: " + javaBean);
                            }
    
                            @Override
                            public void callbackIn(int[] arr) throws RemoteException {
                                Log.d(TAG, "客户端 callbackIn: " + arr[0]);
                                arr[0] = 5;
                                Log.d(TAG, "客户端 callbackIn set 5 value: " + arr[0]);
                            }
    
                            @Override
                            public void callbackOut(int[] arr) throws RemoteException {
                                Log.d(TAG, "客户端 callbackOut: " + arr[0]);
                                arr[0] = 5;
                                Log.d(TAG, "客户端 callbackOut set 5 value: " + arr[0]);
                            }
    
                            @Override
                            public void callbackInout(int[] arr) throws RemoteException {
                                Log.d(TAG, "客户端 callbackInout: " + arr[0]);
                                arr[0] = 5;
                                Log.d(TAG, "客户端 callbackInOut set 5 value: " + arr[0]);
                            }
    
                            @Override
                            public IBinder asBinder() {
                                Log.d(TAG, "客户端 asBinder: ");
                                return this;
                            }
                        });
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }
    }
    

从日志分析问题

oneway解除调用阻塞

boolean setForResult(String message) = 8;
oneway void setOneway(String message) = 9;

public boolean setForResult(String message) throws RemoteException {
    Log.d(TAG, "服务端 setForResult: " + message);

    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return false;
}

@Override
public void setOneway(String message) throws RemoteException {
    Log.d(TAG, "服务端 setOneway: " + message);
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

通过客户端调用服务端接口,在两个方法中均执行2秒的Sleep操作。执行结果

22:57:54.013 12202-12202/com.android D/TestAidlActivity: 客户端 test oneway ----------------
22:57:54.014 12202-12202/com.android D/TestAidlActivity: 客户端 test oneway time: 275200ns
22:57:54.019 12240-12260/com.android D/TestAidlService: 服务端 setOneway: setOneway

22:57:54.021 12240-12253/com.android D/TestAidlService: 服务端 setForResult: message
22:57:56.035 12202-12202/com.android D/TestAidlActivity: 客户端 test result false time: 2020171900ns
22:57:56.035 12202-12202/com.android D/TestAidlActivity: 客户端 test oneway **************** end

从执行结果上看

  1. 设置了oneway的接口立刻就返回了结果,服务端日志在客户端执行完毕之后。用时0.3ms
  2. 没有设置oneway的接口需要等待sleep结束才返回结果,客户端->服务端是同步执行的。用时2.02ms
  3. 使用oneway和没有使用oneway的接口在服务端执行的线程是不一样的。一个线程是12260,一个线程是12253。

方向标记的用处

方向标记里out只有非原语类型支持,下面用int[] arr进行验证,分别声明三种方向标,在客户端传入arr[0] = 1,在服务端重新赋值arr[0] = 3

void setIn(in int[] arr) = 11;
void setOut(out int[] arr) = 12;
void setInout(inout int[] arr) = 13;

客户端调用

try {
    int[] array = new int[]{0, 0};
    array[0] = 1;

    Log.d(TAG, "客户端 test in before: " + array[0]);
    mBinder.setIn(array);
    Log.d(TAG, "客户端 test in after: " + array[0]);

    Log.d(TAG, "客户端 test out before: " + array[0]);
    mBinder.setOut(array);
    Log.d(TAG, "客户端 test out after: " + array[0]);

    Log.d(TAG, "客户端 test inout before: " + array[0]);
    mBinder.setInout(array);
    Log.d(TAG, "客户端 test inout after: " + array[0]);

} catch (RemoteException e) {
    e.printStackTrace();
}

服务端逻辑

public void setIn(int[] arr) throws RemoteException {
    Log.d(TAG, "服务端 setIn: " + arr[0]);
    arr[0] = 3;
    Log.d(TAG, "服务端 setIn 3 value: " + arr[0]);
}

@Override
public void setOut(int[] arr) throws RemoteException {
    Log.d(TAG, "服务端 setOut: " + arr[0]);
    arr[0] = 3;
    Log.d(TAG, "服务端 setOut 3 value: " + arr[0]);
}

@Override
public void setInout(int[] arr) throws RemoteException {
    Log.d(TAG, "服务端 setInout: " + arr[0]);
    arr[0] = 3;
    Log.d(TAG, "服务端 setInout 3 value: " + arr[0]);
}

执行结果

23:18:06.569 12414-12414/com.android D/TestAidlActivity: 客户端 test in before: 1
23:18:06.576 12532-12545/com.android D/TestAidlService: 服务端 setIn: 1
23:18:06.577 12532-12545/com.android D/TestAidlService: 服务端 setIn 3 value: 3
23:18:06.581 12414-12414/com.android D/TestAidlActivity: 客户端 test in after: 1

23:18:06.583 12414-12414/com.android D/TestAidlActivity: 客户端 test out before: 1
23:18:06.590 12532-12545/com.android D/TestAidlService: 服务端 setOut: 0
23:18:06.591 12532-12545/com.android D/TestAidlService: 服务端 setOut 3 value: 3
23:18:06.592 12414-12414/com.android D/TestAidlActivity: 客户端 test out after: 3

23:18:06.593 12414-12414/com.android D/TestAidlActivity: 客户端 test inout before: 3
23:18:06.602 12532-12545/com.android D/TestAidlService: 服务端 setInout: 3
23:18:06.603 12532-12545/com.android D/TestAidlService: 服务端 setInout 3 value: 3
23:18:06.605 12414-12414/com.android D/TestAidlActivity: 客户端 test inout after: 3
  1. in声明的,只能从发送端传递接收端,接收端修改后,不会影响发送端的值
  2. out声明的,只能从接收端传递给发送端,发送端的内容,接收端读不到,接收端设置的内容,发送端可以读到。
  3. inout声明的,发送端和接收端都能读到内容变化。

附带一个从服务端发送给客户端的执行结果

23:23:52.207 12532-12545/com.android D/TestAidlService: 服务端 test in before: 1
23:23:52.208 12414-12414/com.android D/TestAidlActivity: 客户端 callbackIn: 1
23:23:52.208 12414-12414/com.android D/TestAidlActivity: 客户端 callbackIn set 5 value: 5
23:23:52.209 12532-12545/com.android D/TestAidlService: 服务端 test in after: 1

23:23:52.211 12532-12545/com.android D/TestAidlService: 服务端 test out before: 1
23:23:52.211 12414-12414/com.android D/TestAidlActivity: 客户端 callbackOut: 0
23:23:52.211 12414-12414/com.android D/TestAidlActivity: 客户端 callbackOut set 5 value: 5
23:23:52.212 12532-12545/com.android D/TestAidlService: 服务端 test out after: 5

23:23:52.214 12532-12545/com.android D/TestAidlService: 服务端 test inout before: 5
23:23:52.214 12414-12414/com.android D/TestAidlActivity: 客户端 callbackInout: 5
23:23:52.214 12414-12414/com.android D/TestAidlActivity: 客户端 callbackInOut set 5 value: 5
23:23:52.215 12532-12545/com.android D/TestAidlService: 服务端 test inout after: 5

其它相关

AndroidStudio中修改AIDL的引用目录

build.gradle中加入下面代码

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

Android源码编译引入AIDL文件

mk文件加入如下代码

LOCAL_SRC_FILES += $(call all-Iaidl-files-under, aidl)

监听远端对象实例Dead

我们通过onServiceConnect拿到的并不是实际的服务端对象,而实际只是服务端对象的代理。这并不影响使用,但是如果服务福安发生问题,我们仍然使用其引用进行接口调用,则会发生异常。所以我们应该监听对端的状态。可以使用IBinder的linkToDeath方法。

try {
    Log.d(TAG, "onServiceConnected: " + iBinder.getInterfaceDescriptor());
    iBinder.linkToDeath(new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.d(TAG, "客户端 binderDied: ");
        }
    }, 0);
} catch (RemoteException e) {
    e.printStackTrace();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值