客户端服务端配置
1.客户端和服务端需要配置相同的.aidl文件、文件名、包名路径都需要一致

首先在main路径下创建一个aidl的文件夹,里面再放aidl文件
客户端、服务端配置:
package com.myaildtest;
//为了能正常通信,Android 要求客户端和服务端的 aidl 文件包名和文件名必须保持一致
interface IMyAidlService {
int getName();
void addName(in String s); //in代表输入方向
}
设置完毕文件后,点击as的build,使其生成对应的IMyAidlService.java文件,代码如下
public interface IMyAidlService extends android.os.IInterface
{
/** Default implementation for IMyAidlService. */
public static class Default implements com.myaildtest.IMyAidlService
{
@Override public int getName() throws android.os.RemoteException
{
return 0;
}
@Override public void addName(java.lang.String s) throws android.os.RemoteException
{
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.myaildtest.IMyAidlService
{
private static final java.lang.String DESCRIPTOR = "com.myaildtest.IMyAidlService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.myaildtest.IMyAidlService interface,
* generating a proxy if needed.
*/
public static com.myaildtest.IMyAidlService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.myaildtest.IMyAidlService))) {
return ((com.myaildtest.IMyAidlService)iin);
}
return new com.myaildtest.IMyAidlService.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
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getName:
{
data.enforceInterface(descriptor);
int _result = this.getName();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_addName:
{
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
this.addName(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.myaildtest.IMyAidlService
{
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;
}
@Override public int getName() 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);
boolean _status = mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getName();
}
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addName(java.lang.String s) 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(s);
boolean _status = mRemote.transact(Stub.TRANSACTION_addName, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addName(s);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public static com.myaildtest.IMyAidlService sDefaultImpl;
}
static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
public static boolean setDefaultImpl(com.myaildtest.IMyAidlService impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Stub.Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.myaildtest.IMyAidlService getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public int getName() throws android.os.RemoteException;
public void addName(java.lang.String s) throws android.os.RemoteException;
}
可以看到定义的两个方法getAge()和addName()已经是throws android.os.RemoteException的状态了
代码具体解释我们下文再讲
下一步就是客户端和服务端怎么通信了
2.相同的包名下,服务端设置service
package com.myaildtest
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
class MyAidlTestService : Service() {
override fun onBind(intent: Intent): IBinder {
Log.d("aidl TAG","onBind()")
return ContactManagerBinder() //返回对象给客户端,即返回IMyAidlService.Stub()
}
//返回的是一个binder
private class ContactManagerBinder : IMyAidlService.Stub() {
override fun getName(): Int {
return 114514
}
override fun addName(s: String?) {
Log.d("aidl TAG","name:$s")
//todo
}
}
}
我们定义一个Bindler,继承IMyAidlService.Stub,可以看到
public static abstract class Stub extends android.os.Binder implements com.myaildtest.IMyAidlService
Stub类本质就是一个Binder,并实现了我们的aidl接口,我们在这里重写方法,客户端可以调用次方法
3.客户端通过aidl通信,并调用服务端api
客户端定义ServiceConnnection,并startService
private val serviceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
val mIContactsManager = IMyAidlService.Stub.asInterface(service)
Log.d("aidl TAG", "connnected:${mIContactsManager.name},addName:${mIContactsManager.addName("jack")} ")
}
override fun onServiceDisconnected(name: ComponentName) {
Log.d("aidl TAG", "onServiceDisconnected: ")
}
}
val intent = Intent()
intent.setComponent(ComponentName(
"com.example.myapplication", //包名
"com.myaildtest.MyAidlTestService" //service名
))
bindService(intent, serviceConnection, BIND_AUTO_CREATE)
看下调用方式:
在客户端,我们触发bindService()逻辑后,连接到了服务端的Service,触发服务端的onBInder(),并返回给客户端ContactManagerBinder()对象,客户端的onServiceConnected()回调,通过
IMyAidlService.Stub.asInterface(service)得到ContactManagerBinder()对象

我们可以根据log发现,跨进程通信正常了,且和预期的调用顺序一样。那么我们现在就根据IMyAidlService.java源码来了解一下,这个底层逻辑是啥,它到底是什么进行通信的呢?
aidl.java源码分析
aidl.java中最重要的就是Stub类的实现
我看来看逻辑调用顺序,首先就是,客户端的onServiceConnected()中的service:Ibinder参数,我们看上文的代码其实就知道,这个service:Ibinder就是服务端的IMyAidlService.Stub(),得到此参数后,开始调用IMyAidlService.Stub.asInterface(service)
public static com.myaildtest.IMyAidlService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); //查询本地有没有这个接口
if (((iin!=null)&&(iin instanceof com.myaildtest.IMyAidlService))) {
return ((com.myaildtest.IMyAidlService)iin);
}
return new com.myaildtest.IMyAidlService.Stub.Proxy(obj); //没有的话就用远程接口
}
那么我们接下来看com.myaildtest.IMyAidlService.Stub.Proxy(obj)
private static class Proxy implements com.myaildtest.IMyAidlService
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote; //此时这个remote就是服务端的IMyAidlService.Stub()
}
@Override public android.os.IBinder asBinder()
{
//asBinder()得到服务端的IMyAidlService.Stub(),即ContactManagerBinder()
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public int getName() 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);
//这里客户端调用transact,传入_reply参数,服务端回调onTranscat(),此时客户端挂起,等待服务端结果返回
boolean _status = mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
//调用失败就用本地接口的getName()
return getDefaultImpl().getName();
}
//读取结果
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addName(java.lang.String s) 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(s);
//这里客户端调用transact,传入_reply,服务端回调onTranscat(),此时客户端挂起,等待服务端结果返回
boolean _status = mRemote.transact(Stub.TRANSACTION_addName, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
//调用失败就用本地的addName()
getDefaultImpl().addName(s);
return;
}
//读取结果
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public static com.myaildtest.IMyAidlService sDefaultImpl;
}
那么得到Proxy类了,那么下一步的addName()和getName()方法其实在上面的代码也可以看到最终走到了mRemote.transact,此方法最终调用到服务端的onTranscat()
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getName:
{
data.enforceInterface(descriptor);
int _result = this.getName(); //调用本地方法(服务端)得到结果
reply.writeNoException(); //写入无异常
reply.writeInt(_result); //结果回传给客户端
return true;
}
case TRANSACTION_addName:
{
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString(); //读取参数
this.addName(_arg0); //调用本地方法(服务端)得到结果
reply.writeNoException(); //写入无异常
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
以下图的类图表示IMyAidlService.java中所有类关系

本质上Proxy是一个实现了IMyAidlService接口的类,此代理类中核心是mRemote成员,主要操作远程服务端操作,实现的getName()和addName()主要用于调用服务端的接口。
本质上Stub是一个Binder,并且实现了IMyAidlService接口(实际上因为是抽象类,所以没有实现,具体实现是继承了Stub的服务端的类来实现),主要的功能asInterface,会将服务端传过来的binder赋值给Proxy的mRemote。
具体的流程一步步我们用下面的流程图来展示

异常
假设我们在服务端写了一个异常逻辑,那么客户端调用会报错吗?
Server端
private class ContactManagerBinder : IMyAidlService.Stub() {
override fun getName(): Int {
Log.d("aidl TAG","getName start")
val s = intArrayOf(1,2,3,4,5)
s[12]
Log.d("aidl TAG","getName end")
return 114514
}
override fun addName(s: String?) {
Log.d("aidl TAG","name:$s")
//todo
}
}
客户端代码不动,我们可以发现,应用没有闪退,但是getName()返回了0,查看源码可以发现,这时候调用transact是失败的,最终走到了getDefaultImpl().getName()默认方法,即0
@Override public int getName() 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);
//会调用失败
boolean _status = mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getName(); //返回默认实现即0
}
_reply.readException(); //先读取异常,有异常的话readException方法里面会直接抛出
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
同时可以看到堆栈报错信息:
*** Uncaught remote exception! Exceptions are not yet supported across processes. Client PID17734 UID 10339.
java.lang.ArrayIndexOutOfBoundsException: length=5; index=12
at com.myaildtest.MyAidlTestService$ContactManagerBinder.getName(MyAidlTestService.kt:20)
at com.myaildtest.IMyAidlService$Stub.onTransact(IMyAidlService.java:65)
at android.os.Binder.execTransactInternal(Binder.java:1561)
at android.os.Binder.execTransact(Binder.java:1505)
可以看到这个remote exception不能捕获,因为exceptions是不支持跨进程
ok到这里了可能大家都觉得到此为止了,既然服务端exception不能捕获那么就算了,但是实际上Parcel是支持传输exception的
Parcel是支持传输exception的
Parcel是支持传输exception的
查看Parcel我们可以看到,它支持如下异常
public static int getExceptionCode(@NonNull Throwable e) {
int code = 0;
if (e instanceof Parcelable
&& (e.getClass().getClassLoader() == Parcelable.class.getClassLoader())) {
// We only send Parcelable exceptions that are in the
// BootClassLoader to ensure that the receiver can unpack them
code = EX_PARCELABLE;
} else if (e instanceof SecurityException) {
code = EX_SECURITY;
} else if (e instanceof BadParcelableException) {
code = EX_BAD_PARCELABLE;
} else if (e instanceof IllegalArgumentException) {
code = EX_ILLEGAL_ARGUMENT;
} else if (e instanceof NullPointerException) {
code = EX_NULL_POINTER;
} else if (e instanceof IllegalStateException) {
code = EX_ILLEGAL_STATE;
} else if (e instanceof NetworkOnMainThreadException) {
code = EX_NETWORK_MAIN_THREAD;
} else if (e instanceof UnsupportedOperationException) {
code = EX_UNSUPPORTED_OPERATION;
} else if (e instanceof ServiceSpecificException) {
code = EX_SERVICE_SPECIFIC;
}
return code;
}
只支持上述异常,所以我设置的越界异常,捕获不到,那么我们改成空指针异常来试试看呢?
服务端修改:
override fun getName(): Int {
Log.d("aidl TAG","getName start")
val s:String?= null
s!!.length
return 114514
}
嗯。空指针异常被捕获到了,然后闪退
FATAL EXCEPTION: main
Process: com.example.otherprocess, PID: 20637
java.lang.NullPointerException
at android.os.Parcel.createExceptionOrNull(Parcel.java:3262)
at android.os.Parcel.createException(Parcel.java:3240)
at android.os.Parcel.readException(Parcel.java:3223)
at android.os.Parcel.readException(Parcel.java:3165)
at com.myaildtest.IMyAidlService$Stub$Proxy.getName(IMyAidlService.java:111)
at com.example.otherprocess.MainActivity$serviceConnection$1.onServiceConnected(MainActivity.kt:67)
at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:2333)
at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:2372)
at android.os.Handler.handleCallback(Handler.java:1014)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loopOnce(Looper.java:250)
at android.os.Looper.loop(Looper.java:340)
at android.app.ActivityThread.main(ActivityThread.java:9882)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:621)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:957)
这时候就需要客户端添加try catch保护了
AIDL进阶
看了上述的分析其实大概了解通信原理了,实际上就是通过远程的binder,写入data、replay参数后调用transact()后再调用到onTransact(),在onTransact()中进行计算最后再把结果通过replay.write写入,客户端就可以通过这个Parcelable参数读取到结果了。那么我们可以自定义写一个binder通信,不用aidl实现
Server端
class MyBinder: Binder(), MathMethod {
companion object {
const val PLUS = 0
const val MINUS = 1
}
override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {
when(code) {
PLUS -> {
data.enforceInterface("plus")
val p1 = data.readInt()
val p2 = data.readInt()
val result = this.plus(p1,p2)
reply?.writeInterfaceToken("plus") //一定要放在writeNoException()之前
reply?.writeNoException()
reply?.writeInt(result)
return true
}
MINUS -> {
data.enforceInterface("minus")
val p1 = data.readInt()
val p2 = data.readInt()
val result = this.minus(p1,p2)
reply?.writeInterfaceToken("minus")
reply?.writeNoException()
reply?.writeInt(result)
return true
}
}
return super.onTransact(code, data, reply, flags)
}
override fun plus(a: Int, b: Int):Int {
return a+b
}
override fun minus(a: Int, b: Int): Int {
return a-b
}
}
interface MathMethod{
fun plus(a:Int, b:Int):Int
fun minus(a:Int, b:Int):Int
}
首先实现一个自己的binder,不需要再继承Stub()类,在方法中我们自己实现plus和minus方法,在onTransact方法中读取参数,然后进行操作,结果通过replay.write写入。
我们再来看客户端核心代码:
client端
private val serviceConnection2: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
Log.d("aidl TAG", "serviceConnected2 ")
val data = Parcel.obtain()
val reply = Parcel.obtain()
data.writeInterfaceToken("plus")
data.writeInt(10)
data.writeInt(20)
val status = service.transact(PLUS,data,reply,0)
reply.enforceInterface("plus") //一定要放在readException()之前
reply.readException()
Log.d("aidl TAG", "plus result:${reply.readInt()}")
}
override fun onServiceDisconnected(name: ComponentName) {
Log.d("aidl TAG", "onServiceDisconnected: ")
}
}
我们拿到service的IBinder对象后,data写入参数,然后核心通过service.transact调用服务端逻辑,然后服务端运算完毕后,通过replay.read得到结果。
那么如果我们需要传递的参数并不是普通参数呢?这该怎么办???
代码如下,只要稍微做一下改动即可,因为传入的参数是Parcelable的对象,那么需要这么写:
client端
val p = Person("xiaoming",40,"boy")
p.writeToParcel(data,0) //核心方法,使用writeToParcel,引用对象是新建的对象类
val status = service.transact(114514,data,reply,0)
reply.enforceInterface("person")
reply.readException()
val newp = Person.createFromParcel(reply) //核心方法,使用 Person.createFromParcel,将返回的replay中数据读取,生成新对象
server端
val p = Person.createFromParcel(data) //核心方法,传入的data参数提取对象p
val newP = getPerson(p)
reply?.writeInterfaceToken("person")
reply?.writeNoException()
newP.writeToParcel(reply!!,0) //核心方法,将对象写入到reply中
return true
额外补充小知识
1.客户端调用proxy.getName(),最后走到实际上走的是binder线程,所以服务端不能做耗时操作
client端
private val serviceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
//main线程
}
override fun onServiceDisconnected(name: ComponentName) {
//main线程
}
}
server端
private class ContactManagerBinder : IMyAidlService.Stub() {
override fun getName(): Int {
//binder线程,客户端的主线程,所以这里不能做耗时操作,会卡主客户端
return 114514
}
override fun addName(s: String?) {
//binder线程,客户端的主线程
}
}
如果要做耗时操作,那么可以给aidl文件的参数方法名加oneway前缀进行异步调用,参考文章https://juejin.cn/post/7471652290460696576
2.有时候异常堆栈有caused by但有时候又没有,这是什么原因呢?因为catch层层包装了一下,在catch中又throw了异常,一般用于纠正问题根本点
private fun testCatch3() {
try {
testCatch2()
} catch (e: MyIllegalArgumentException) {
throw MyIndexOutException("index out bound",e)
}
}
private fun testCatch2() {
try {
testCatch1()
} catch (e: MyRuntimeException) {
throw MyIllegalArgumentException("arg is error",e)
}
}
private fun testCatch1(){
var s:String? = null
throw MyRuntimeException("s is null ,error")
}
可以看到testCatch1抛出来MyRuntimeException,然后别testCatch2捕获,并抛出来一个MyIllegalArgumentException,最终被testCatch3捕获,抛出来MyIndexOutException。查看log日志台
FATAL EXCEPTION: main
Process: com.example.otherprocess, PID: 20097
com.example.otherprocess.trycatch.TryCatchActivity$MyIndexOutException: index out bound
at com.example.otherprocess.trycatch.TryCatchActivity.testCatch3(TryCatchActivity.kt:36)
at com.example.otherprocess.trycatch.TryCatchActivity.onCreate$lambda$1(TryCatchActivity.kt:28)
at com.example.otherprocess.trycatch.TryCatchActivity.$r8$lambda$6AI6dCd7QLyrN-ZTF5C4dKTqkYk(Unknown Source:0)
at com.example.otherprocess.trycatch.TryCatchActivity$$ExternalSyntheticLambda1.onClick(Unknown Source:2)
at android.view.View.performClick(View.java:8140)
at android.view.View.performClickInternal(View.java:8117)
at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0)
at android.view.View$PerformClick.run(View.java:32132)
at android.os.Handler.handleCallback(Handler.java:1014)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loopOnce(Looper.java:250)
at android.os.Looper.loop(Looper.java:340)
at android.app.ActivityThread.main(ActivityThread.java:9882)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:621)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:957)
Caused by: com.example.otherprocess.trycatch.TryCatchActivity$MyIllegalArgumentException: arg is error
at com.example.otherprocess.trycatch.TryCatchActivity.testCatch2(TryCatchActivity.kt:44)
at com.example.otherprocess.trycatch.TryCatchActivity.testCatch3(TryCatchActivity.kt:34)
... 15 more
Caused by: com.example.otherprocess.trycatch.TryCatchActivity$MyRuntimeException: s is null ,error
at com.example.otherprocess.trycatch.TryCatchActivity.testCatch1(TryCatchActivity.kt:50)
at com.example.otherprocess.trycatch.TryCatchActivity.testCatch2(TryCatchActivity.kt:42)
... 16 more
可以看到有三种异常,我们不必慌,其实异常是层层往上抛的,只要关注最底层的Caused by即可,这就是抛出问题的最原始位置啦。
注意:写自定义Exception的没必要继承已有的空指针、数组越界异常,没有意义,需要自己继承RuntimeException,然后回抛的时候,参数cause一定要带上,例如
throw MyIllegalArgumentException("arg is error",e)
否则caused by不会打印

21万+

被折叠的 条评论
为什么被折叠?



