Android AIDL通信DeadObjectException解决方法示例

作者:尹学姐 时间:2022-06-04 14:53:27 

崩溃来源

使用过AIDL进行跨进程通信的同学,肯定遇到过DeadObjectException这个崩溃,那么这个崩溃是怎么来的,我们又该如何解决它呢?今天这篇文章就来聊一聊。

首先,这个崩溃的意思是,多进程在进行跨进程Binder通信的时候,发现通信的Binder对端已经死亡了。

抛出异常的Java堆栈最后一行是BinderProxy.transactNative,所以我们从这个方法入手,看看崩溃是在哪里产生的。

很显现,transactNative对应的是一个native方法,我们找到对应的native方法,在android_util_Binder.cpp中。

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
       jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
   // 如果data数据为空,直接抛出空指针异常
   if (dataObj == NULL) {
       jniThrowNullPointerException(env, NULL);
       return JNI_FALSE;
   }
   // 将Java层传入的对象转换为C++层的指针,如果转换出错,中断执行,返回JNI_FALSE
   Parcel* data = parcelForJavaObject(env, dataObj);
   if (data == NULL) {
       return JNI_FALSE;
   }
   Parcel* reply = parcelForJavaObject(env, replyObj);
   if (reply == NULL && replyObj != NULL) {
       return JNI_FALSE;
   }
   // 获取C++层的Binder代理对象指针
   // 如果获取失败,会抛出IllegalStateException
   IBinder* target = getBPNativeData(env, obj)->mObject.get();
   if (target == NULL) {
       jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
       return JNI_FALSE;
   }
   // 调用BpBinder对象的transact方法
   status_t err = target->transact(code, *data, reply, flags);
   // 如果成功,返回JNI_TRUE,如果失败,返回JNI_FALSE
   if (err == NO_ERROR) {
       return JNI_TRUE;
   } else if (err == UNKNOWN_TRANSACTION) {
       return JNI_FALSE;
   }
   // 处理异常情况的抛出
   signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/, data->dataSize());
   return JNI_FALSE;
}

可以看到,这个方法主要做的事情是:

  • Java层传入的data,转换成C++层的指针

  • 获取C++层的Binder代理对象

  • 调用BpBinder对象的transact方法

  • 处理transact的结果,抛出异常

接下来我们看看,BpBindertransact方法。

status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
   // 首先判断Binder对象是否还存活,如果不存活,直接返回DEAD_OBJECT
   if (mAlive) {
           ...
           status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags);
           return status;
   }
   return DEAD_OBJECT;
}

transact的具体方法,我们这里先不讨论。我们可以看到,在这里会判断当前的Binder对象是否alive,如果不alive,会直接返回DEAD_OBJECT的状态。

返回的结果,在android_util_BindersignalExceptionForError中处理。

void signalExceptionForError(JNIEnv* env, jobject obj, status_t err,
       bool canThrowRemoteException, int parcelSize)
{
      // 省略其他异常处理的代码
       ....
       case DEAD_OBJECT:
           // DeadObjectException is a checked exception, only throw from certain methods.
           jniThrowException(env, canThrowRemoteException
                   ? "android/os/DeadObjectException"
                           : "java/lang/RuntimeException", NULL);
           break;
}

这个方法,其实包含非常多异常情况的处理。为了看起来更清晰,这里我们省略了其他异常的处理逻辑,只保留了DEAD_OBJECT的处理。可以很明显的看到,在这里我们抛出了DeadObjectException异常。

解决方法

通过前面的源码分析,我们知道DeadObjectException是发生在,当我们调用transact接口发现Binder对象不再存活的情况。

解决方案也很简单,就是当这个Binder对象死亡之后,不再调用transact接口。

方法1 调用跨进程接口之前,先判断Binder是否存活

这个方案比较简单粗暴,就是在多有调用跨进程接口的地方,都加一个Binder是否存活的判断。

if (mService != null && mService.asBinder().isBinderAlive()) {
           mService.test();
       }

我们来看下isBinderAlive的源码,就是判断mAlive标志位是否为0。

bool BpBinder::isBinderAlive() const
{
   return mAlive != 0;
}

方法2 监听Binder死亡通知

先初始化一个DeathRecipient,用来监听死亡通知。

private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
       @Override
       public void binderDied() {
           // 解绑当前监听,重新启动服务
           mService.asBinder().unlinkToDeath(mDeathRecipient, 0);
           if (mService != null)
               bindService(new Intent("com.service.bind"), mService, BIND_AUTO_CREATE);
       }
   };

在这个死亡监听里,我们可以选择几种处理方式:

  • 什么都不做,直接将mService设置为空

  • 再次尝试启动和绑定服务

onServiceConnected方法中,注册死亡监听:

public void onServiceConnected(ComponentName name, IBinder service) {          
   mService = IServiceInterface.Stub.asInterface(service);    
   //获取服务端提供的接口
   try {
       // 注册死亡代理
       if(mService != null){
           service.linkToDeath(mDeathRecipient, 0);
       }      
   } catch (RemoteException e) {
       e.printStackTrace();
   }
}

来源:https://juejin.cn/post/7208111879150551096

标签:Android,AIDL,通信,DeadObjectException
0
投稿

猜你喜欢

  • Java编程复用类代码详解

    2021-09-13 10:06:34
  • java实现文件上传下载

    2023-11-23 09:41:18
  • Java Thread之Sleep()使用方法及总结

    2023-11-16 10:38:35
  • Java实现雪花算法(snowflake)

    2022-10-27 03:43:23
  • c#实现输出的字符靠右对齐的示例

    2023-02-26 12:23:39
  • java实现网站微信扫码支付

    2023-06-18 18:58:19
  • 异常try catch的常见四类方式(案例代码)

    2023-11-10 23:18:00
  • mybatis-plus分页查询的实现示例

    2023-11-25 04:57:57
  • SpringBoot使用WebSocket实现前后端交互的操作方法

    2022-07-06 12:34:35
  • java 中类似js encodeURIComponent 函数的实现案例

    2023-03-20 13:13:50
  • Android实现状态栏和虚拟按键背景颜色的变化实例代码详解

    2023-02-07 00:17:15
  • java中归并排序和Master公式详解

    2022-03-30 08:53:19
  • Android 使用PDF.js浏览pdf的方法示例

    2021-12-08 06:11:41
  • OpenGL绘制贝塞尔曲线

    2022-02-28 11:51:57
  • 十分钟速懂java知识点 System类

    2022-11-25 04:19:26
  • 深入了解Java中finalize方法的作用和底层原理

    2023-06-18 00:31:35
  • 快速了解c# 结构体

    2022-10-19 05:38:45
  • 浅谈spring的重试机制无效@Retryable@EnableRetry

    2021-12-12 10:42:57
  • Android RecyclerView显示Item布局不一致解决办法

    2023-04-25 06:50:32
  • Java身份证验证方法实例详解

    2023-04-04 02:22:11
  • asp之家 软件编程 m.aspxhome.com