文章目录
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
方向标记
输入类型,调用端到接收端。基本数据类型(byte
、char
、int
、long
、float
、double
、boolean
)和String
、CharSequence
只能使用in
,在文件中可以不显示指定。out
方向标记
输出类型,接收端到输入端inout
方向标记
双向支持oneway
oneway
可以用来修饰在interface
之前,这样会造成interface
内所有的方法都隐式地带上oneway
;oneway
也可以修饰在interface
里的各个方法之前。- 被oneway修饰了的方法不可以有返回值,也不可以有带
out
或inout
的参数。
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);
除了基本数据类型(byte
、char
、int
、long
、float
、double
、boolean
)和String
、CharSequence
外,所有参数都需要显示指定参数的方向标记in
、out
、inout
中的一种
引用
即使在与接口相同的包内定义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或者都不指定。
总结
定义服务接口时需要注意:
-
方法可带零个或多个参数,返回值或空值。
-
所有非原语参数均需要指示数据走向的方向标记。这类标记可以是
in
、out
、inout
,原语默认为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()
回调方法的参数中获取。关键函数为Stub
中 asInterface()
方法。
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
协议的类,必须执行以下操作:
- 让您的类实现
Parcelable
接口。 - 实现
writeToParcel
,它会获取对象的当前状态并将其写入Parcel
。 - 为您的类添加
CREATOR
静态字段,该字段是实现Parcelable.Creator
接口的对象。 - 最后,创建声明 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
从执行结果上看
- 设置了
oneway
的接口立刻就返回了结果,服务端日志在客户端执行完毕之后。用时0.3ms - 没有设置
oneway
的接口需要等待sleep
结束才返回结果,客户端->服务端是同步执行的。用时2.02ms - 使用
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
in
声明的,只能从发送端传递接收端,接收端修改后,不会影响发送端的值out
声明的,只能从接收端传递给发送端,发送端的内容,接收端读不到,接收端设置的内容,发送端可以读到。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();
}