当前位置: 首页 > news >正文

android aidl相关学习

客户端服务端配置

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{}@Overridepublic 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.myaildtestimport android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Logclass MyAidlTestService : Service() {override fun onBind(intent: Intent): IBinder {Log.d("aidl TAG","onBind()")return ContactManagerBinder()  //返回对象给客户端,即返回IMyAidlService.Stub()}//返回的是一个binderprivate 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。
具体的流程一步步我们用下面的流程图来展示

异常

假设我们在服务端写了一个异常逻辑,那么客户端调用会报错吗?

Serverprivate 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=12at 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 themcode = 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?= nulls!!.lengthreturn 114514}

嗯。空指针异常被捕获到了,然后闪退

FATAL EXCEPTION: main
Process: com.example.otherprocess, PID: 20637
java.lang.NullPointerExceptionat 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保护了

额外补充小知识

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? = nullthrow 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 boundat 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 errorat 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 ,errorat 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不会打印

http://www.dtcms.com/a/336604.html

相关文章:

  • 【昇腾】单张48G Atlas 300I Duo推理卡MindIE+WebUI方式跑14B大语言模型_20250817
  • 在鸿蒙中实现深色/浅色模式切换:从原理到可运行 Demo
  • 母猪姿态转换行为识别:计算机视觉与行为识别模型调优指南
  • redis和cdn的相似性和区别
  • 编程算法实例-最小公倍数
  • Python自学09-常用数据结构之元组
  • 黑马商城day08-Elasticsearch作业(个人记录、仅供参考、详细图解)
  • 嵌入式系统中的签名验证:设计与原理解析(C/C++代码实现)
  • Java基础Object中常见问题解析
  • Redis面试精讲 Day 24:Redis实现限流、计数与排行榜
  • 数字货币的法律属性与监管完善路径探析
  • SCAI采用公平发射机制成功登陆LetsBonk,60%代币供应量已锁仓
  • SpringBoot中,接口加解密
  • C语言课程开发
  • 【前端基础】flex布局中使用`justify-content`后,最后一行的布局问题
  • Java 基础 -- Java 基础知识
  • 2025-08-17 李沐深度学习18——循环神经网络基础
  • Spring Cloud系列—Seata部署
  • 照相机标定-动手学计算机视觉16
  • easya2a: 一键将 LangChain Agent 发布为 A2A 服务
  • Matlab数字图像处理——基于BM4D压缩感知的三维图像信号重构算法
  • 知识点汇集-web
  • 第三十八天(Node.JS)
  • 【LeetCode 热题 100】(八)二叉树
  • 如何使用java写一个agent
  • 说一下分离读写
  • c_str()函数的详细解析
  • 力扣438:找到字符串中所有的字母异位词
  • ACCESS/SQL SERVER保存软件版本号为整数类型,转成字符串
  • 第13章《远程处理:一对一及一对多》——PowerShell Remoting 学习笔记