RemoteServiceException: can‘t deliver broadcast 问题分析

news/2024/11/28 23:33:45/

一、问题背景
最近测试跑monkey报了一个应用崩溃的log,核心堆栈如下(已脱敏,出问题的android系统版本是api11,AndroidR):
ps: 本次涉及的应用包名统一用com.my.app代替

11-28 03:57:20.326 12039 12039 E AndroidRuntime: FATAL EXCEPTION: main
11-28 03:57:20.326 12039 12039 E AndroidRuntime: Process: com.my.app, PID: 12039
11-28 03:57:20.326 12039 12039 E AndroidRuntime: android.app.RemoteServiceException: can't deliver broadcast
11-28 03:57:20.326 12039 12039 E AndroidRuntime: 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2073)
11-28 03:57:20.326 12039 12039 E AndroidRuntime: 	at android.os.Handler.dispatchMessage(Handler.java:111)
11-28 03:57:20.326 12039 12039 E AndroidRuntime: 	at android.os.Looper.loop(Looper.java:250)
11-28 03:57:20.326 12039 12039 E AndroidRuntime: 	at android.app.ActivityThread.main(ActivityThread.java:7860)
11-28 03:57:20.326 12039 12039 E AndroidRuntime: 	at java.lang.reflect.Method.invoke(Native Method)
11-28 03:57:20.326 12039 12039 E AndroidRuntime: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:591)
11-28 03:57:20.326 12039 12039 E AndroidRuntime: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1123)

网上查了一番,发现有一些前人的分析:

  1. viewpager嵌套fragment或其他大量动态广播注册注销出现的崩溃: https://blog.csdn.net/qq_27381325/article/details/82811079

咋一眼看着像是系统问题,刚开始抛给系统看了,结果系统讲这很可能是应用成使用广播传输的数据量大导致framework层报出的异常。
不过可以初步看到RemotionServiceException可以先推测跟binder跨进程服务有关。
这里错误message的提醒是传递广播不正确,看下sendBroadcast()方法的定义,确实是有可能抛出RemoteServiceException异常。

android.app.ContextImpl#sendBroadcast(android.content.Intent)源码定义:

   @Overridepublic void sendBroadcast(Intent intent) {warnIfCallingFromSystemProcess();String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());try {intent.prepareToLeaveProcess(this);ActivityManager.getService().broadcastIntentWithFeature(mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,false, getUserId());} catch (RemoteException e) { // 这里有声明抛出RemoteException异常,说明发送广播本身就可能抛出异常。throw e.rethrowFromSystemServer();}}

android.os.RemoteException#rethrowFromSystemServer:

    @NonNullpublic RuntimeException rethrowFromSystemServer() {if (this instanceof DeadObjectException) {throw new RuntimeException(new DeadSystemException());} else {throw new RuntimeException(this);}}

再看RemoteExcetion和本次的RemoteServiceException有没有继承关系,发现是没有的,看来这里的异常声明和这次崩溃的堆栈可能关系不大,以下是类的继承关系:
在这里插入图片描述
发现和RemoteServiceException没有继承关系。

然后想要知道这个异常从哪儿报出来的,通过https://cs.android.com/直接搜索“can’t deliver broadcast”,发现能搜出来,google的源码查看网站果然强大,瞬间感觉了有了源码查看利器!
在这里插入图片描述
简单遴选下,发现这个错误最终是在BroadcastQueue中报出来的。
com.android.server.am.BroadcastQueue#performReceiveLocked:

    void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,Intent intent, int resultCode, String data, Bundle extras,boolean ordered, boolean sticky, int sendingUser)throws RemoteException {// Send the intent to the receiver asynchronously using one-way binder calls.if (app != null) {if (app.thread != null) {// If we have an app thread, do the call through that so it is// correctly ordered with other one-way calls.try {app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,data, extras, ordered, sticky, sendingUser, app.getReportedProcState());// TODO: Uncomment this when (b/28322359) is fixed and we aren't getting// DeadObjectException when the process isn't actually dead.//} catch (DeadObjectException ex) {// Failed to call into the process.  It's dying so just let it die and move on.//    throw ex;} catch (RemoteException ex) {// Failed to call into the process. It's either dying or wedged. Kill it gently.synchronized (mService) {Slog.w(TAG, "Can't deliver broadcast to " + app.processName+ " (pid " + app.pid + "). Crashing it.");// 这里通过scheduleCrash抛出去的message跟本次要追踪的吻合。// 可以确定就是从这里抛出去的异常。app.scheduleCrash("can't deliver broadcast");}throw ex;}} else {// Application has died. Receiver doesn't exist.throw new RemoteException("app.thread must not be null");}} else {receiver.performReceive(intent, resultCode, data, extras, ordered,sticky, sendingUser);}}

com.android.server.am.ProcessRecord#scheduleCrash:
可以看到ProcessRecord#scheduleCrash()是通过ApplicationThread抛给了应用层,ApplicationThread接口是SystemServer进程和应用进程通信的AIDL接口,定义在ActivityThread.java文件中。

    void scheduleCrash(String message) {// Checking killedbyAm should keep it from showing the crash dialog if the process// was already dead for a good / normal reason.if (!killedByAm) {if (thread != null) {if (pid == Process.myPid()) {Slog.w(TAG, "scheduleCrash: trying to crash system process!");return;}long ident = Binder.clearCallingIdentity();try {thread.scheduleCrash(message);} catch (RemoteException e) {// If it's already dead our work is done. If it's wedged just kill it.// We won't get the crash dialog or the error reporting.kill("scheduleCrash for '" + message + "' failed",ApplicationExitInfo.REASON_CRASH, true);} finally {Binder.restoreCallingIdentity(ident);}}}}

android.app.ActivityThread.ApplicationThread#scheduleCrash:
通过该方法将framework层system_server进程的异常传递给了应用进程,并通过Handler的方式跑到了主线程。

public void scheduleCrash(String msg) {sendMessage(H.SCHEDULE_CRASH, msg);
}

由此,堆栈是能对应上了。


http://www.ppmy.cn/news/2528.html

相关文章

win11摄像头黑了用不了的七个解决办法

目录 前言必读 方法一、重置和隐式设置摄像头 方法二、更新windwos驱动 方法三、检查串行总线控制器 方法四、下载驱动精灵来安装驱动 方法五、驱动精灵里面修复 方法六、检查键盘上面有没有物理摄像头按键 方法七、使用万能摄像头 前言必读 读者手册(必读&…

给你讲明白MySQL的乐观锁和悲观锁

乐观锁与悲观锁是一种广义上的概念。不管是 Java 语言,也或者是其他语言以及数据库都有这类概念对应的实际应用。想要学习乐观锁和悲观锁就要学习他们的基本知识,那么下面我们来学习一下。 锁 生活中:锁在我们身边无处不在,比如我…

【Zookeeper】学习笔记(二)

Zookeeper学习笔记四、客户端命令4.1、新增节点4.2、查询节点信息4.3、节点类型4.4、更新节点4.5、删除节点4.6、监听器五、SpringBOOT整合Zookeeper六、写数据流程6.1、写流程之写入请求直接发送给Leader节点6.2、写流程之写入请求发送给follower节点七、服务器动态上下线监听…

Spring从入门到精通(二)

文章目录1.动态代理1.1 概念1.2 jdk动态代理(重点)1.3 基于子类的动态代理(了解)2.AOP2.1 概念2.2 springAop — 基于AspectJ技术2.2.1 AspectJ使用(XML)2.2.2 AspectJ使用(注解开发&#xff09…

c语言---指针进阶(2)--玩转指针

今天内容不多,但都是精华。 1.数组参数和指针参数 2.函数指针 2.1笔试题 3.函数指针数组 1.数组参数和指针参数 例1:一维数组传参 void test(int arr[]) {} void test(int arr[10]) {} void test(int *arr) {}void test2(int *arr2[20]) {} void …

Verilog语法之generate for、generate if、generate case

0、前言 Verilog-2005中有3个generate 语句可以用来很方便地实现重复赋值和例化(generate for)或根据条件选择性地进行编译(generate if和generate case)等功能。接下来就一起看下这3个语句的应用场景和应用方法吧。 1、generate …

Laravel 某个固定数据排序在列表顶部sql查询(自定义排序)

业务需要列表中某个固定用户数据处于列表顶部,该用户author状态有多个值(例如:1-999),需要置于顶部的为中间的某个值(例如:author68) 实现方式: 1、判断 author的值是不…

Web服务器通信原理

电脑: Windows 系统 微软开发 闭源 Linux 开源的系统(无数的子系统) Mac os 苹果系统 WEB 安全 > WEB的安全(网站安全) 服务器: 就是一台不关机的电…