Android 7系统异常问题排查(七)应用异常(下)—APK Java层崩溃
系列目录第一篇异常机制全景图 | 第二篇Kernel Panic 与系统重启 | 第三篇Tombstone 机制 | 第四篇System Server Watchdog | 第五篇System Server 崩溃 | 第六篇ANR 机制 | 第七篇Java 层崩溃 | 第八篇Trace 机制 | 第九篇日志系统 | 第十篇实战方法论一、Java 层崩溃用户最熟悉的异常“抱歉XXX 已停止运行”——这是 Android 用户最熟悉的对话框之一也是 App 开发者最常处理的异常。与 ANR主线程不响应不同Java 崩溃是主线程或子线程遇到了未捕获的异常进程直接终止。崩溃 vs ANR 对比维度ANRJava Crash触发原因主线程长时间阻塞未捕获异常NPE、数组越界等用户感知应用无响应对话框已停止运行对话框进程状态仍存活但无响应进程被系统杀死日志traces.txt dropbox(anr)dropbox(crash) logcat恢复方式用户选择等待或确定用户重新打开 App二、崩溃处理链路全景2.1 从异常抛出到进程终止App 代码抛出异常如 NullPointerException │ ▼ JVM 沿调用栈向上查找 catch 块 │ ├─ 找到匹配的 catch → 异常被捕获 → 正常处理不崩溃 │ └─ 未找到 catch → 到达线程顶层 │ ▼ Thread.dispatchUncaughtException() │ ▼ ThreadGroup.uncaughtException() │ ▼ RuntimeInit.KillApplicationHandler.uncaughtException() │ ├─ 1. 打印 FATAL EXCEPTION 到 logcat ├─ 2. 通知 AMS.handleApplicationCrash() │ └─ AMS 收集 CrashInfo → 写入 DropBox │ └─ AMS 弹出 FC 对话框AppErrorDialog ├─ 3. Process.killProcess(Process.myPid()) └─ 4. System.exit(10)2.2 关键源码KillApplicationHandler// frameworks/base/core/java/com/android/internal/os/RuntimeInit.javaprivatestaticclassKillApplicationHandlerimplementsThread.UncaughtExceptionHandler{OverridepublicvoiduncaughtException(Threadt,Throwablee){try{// 防止递归崩溃handler 自身异常if(mCrashing)return;mCrashingtrue;// 1. 确保异常信息记录到 logcatensureLogging(t,e);// 2. 通知 AMSif(mApplicationObject!null){ActivityManagerNative.getDefault().handleApplicationCrash(mApplicationObject,newApplicationErrorReport.CrashInfo(e));}}catch(Throwablet2){// 连 AMS 都联系不上时至少保留日志if(t2instanceofDeadObjectException){// System Server 也在崩溃放弃通知}}finally{// 3. 杀死当前进程Process.killProcess(Process.myPid());System.exit(10);}}}三、AMS 端处理流程3.1 handleApplicationCrash()// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.javapublicvoidhandleApplicationCrash(IBinderapp,ApplicationErrorReport.CrashInfocrashInfo){// 1. 找到崩溃进程的信息ProcessRecordrfindAppProcess(app,Crash);finalStringprocessNameappnull?system_server:rnull?unknown:r.processName;// 2. 写入 DropBoxManagerServiceaddErrorToDropBox(crash,r,processName,null,null,null,null,null,crashInfo);// 3. 处理崩溃进程的关闭crashApplication(r,crashInfo);}voidcrashApplication(ProcessRecordr,ApplicationErrorReport.CrashInfocrashInfo){// 记录崩溃时间r.crashingtrue;r.crashCount;// 决定是否弹出 FC 对话框if(r.crashCount2){// 前两次崩溃弹出对话框让用户知道MessagemsgmHandler.obtainMessage(SHOW_ERROR_MSG);msg.objnewAppErrorResult.Data(r,/* ... */);mHandler.sendMessage(msg);}else{// 连续多次崩溃不再弹框避免骚扰用户// 直接杀进程}}3.2 FC 对话框AppErrorDialog// frameworks/base/services/core/java/com/android/server/am/AppErrorDialog.javaclassAppErrorDialogextendsBaseErrorDialog{// 显示内容// 标题应用名// 内容抱歉XXX 已停止运行。// 按钮[确定]protectedvoidonCreate(BundlesavedInstanceState){// 从 ProcessRecord 和 CrashInfo 中提取信息setTitle(mProc.info.loadLabel(mContext.getPackageManager()));setMessage(mContext.getString(com.android.internal.R.string.application_error,mProc.processName));setButton(DialogInterface.BUTTON_POSITIVE,mContext.getText(com.android.internal.R.string.ok),...);}}3.3 crashCount 保护机制第一次崩溃 → 弹出 FC 对话框 第二次崩溃 → 弹出 FC 对话框 第三次及以上 → 不再弹框静默杀进程 设计目的防止 App 启动即崩溃反复弹框骚扰用户四、Crash 日志解读4.1 logcat 中的 FATAL EXCEPTIONE/AndroidRuntime(12345): FATAL EXCEPTION: main E/AndroidRuntime(12345): Process: com.example.app, PID: 12345 E/AndroidRuntime(12345): java.lang.NullPointerException: Attempt to invoke virtual method int java.lang.String.length() on a null object reference E/AndroidRuntime(12345): at com.example.app.MainActivity.loadData(MainActivity.java:42) E/AndroidRuntime(12345): at com.example.app.MainActivity.onCreate(MainActivity.java:28) E/AndroidRuntime(12345): at android.app.Activity.performCreate(Activity.java:5990) E/AndroidRuntime(12345): ...格式解析异常类名异常消息 at 包名.类名.方法名(文件名:行号) ... Caused by: 异常类名异常消息 ← 异常链如果有 at ...4.2 DropBox 中的 Crash 信息adb shell dumpsys dropbox data_app_crash--print输出包含更完整的信息Tag: data_app_crash Process: com.example.app Flags: 0x28... Package: com.example.app Subject: com.example.app Build: Android/aosp_arm64/generic:7.0/... java.lang.NullPointerException: Attempt to invoke ... at com.example.app.MainActivity.loadData(MainActivity.java:42) ...4.3 混淆后的堆栈还原生产环境的 APK 通常经过 ProGuard/R8 混淆堆栈中显示的是混淆后的类名和方法名at a.b.c.a(Unknown Source)需要配合mapping.txt文件还原# 使用 retrace 工具还原retrace mapping.txt stacktrace.txtdecoded.txt# 或者使用 Android SDK 自带的 proguardgui# 工具路径sdk/tools/proguard/bin/proguardgui.sh五、常见崩溃类型速查5.1 NullPointerException日志特征NullPointerException: Attempt to invoke ... on a null object reference 常见原因 - 对象未初始化就使用 - findViewById() 返回 null布局 ID 不匹配 - getIntent().getExtras() 返回 null - 多线程并发导致的空指针 定位方法 - 从堆栈第一行找到变量名 - 前向追踪该变量的赋值路径5.2 ClassCastException日志特征ClassCastException: XXX cannot be cast to YYY 常见原因 - Fragment 类型转换错误 - Context 类型转换错误 - 泛型擦除导致的类型错误 定位方法 - 检查 XML 中声明的 Fragment 类名 - 检查 getSystemService() 的传入参数5.3 IndexOutOfBoundsException日志特征IndexOutOfBoundsException: Invalid index X, size is Y 常见原因 - 空列表直接 get(0) - 多线程并发修改集合 - RecyclerView 的 Adapter 数据与 UI 不同步 定位方法 - 堆栈行号定位到具体操作 - 检查该列表的数据来源和修改时机5.4 WindowManager.BadTokenException日志特征BadTokenException: Unable to add window -- token null is not valid 常见原因 - Activity 已销毁后尝试 show Dialog - 使用 ApplicationContext 启动 Dialog - 在 onCreate() 中尝试 PopupWindow 定位方法 - Dialog.show() 必须在 Activity 可见时调用 - 或使用 DialogFragment 管理生命周期5.5 DeadObjectException / RemoteException日志特征DeadObjectException / RemoteException 常见原因 - 跨进程调用时对端进程已死亡 - Service 被系统杀死后仍尝试调用 - Binder 连接断开 定位方法 - 检查是否在 ServiceConnection.onServiceDisconnected() 中做了清理 - 使用 DeathRecipient 监听 Binder 死亡5.6 SecurityException日志特征SecurityException: Permission Denial 常见原因 - AndroidManifest.xml 缺少权限声明 - Android 6.0 运行时权限未授予 - 访问其他 App 的私有组件 定位方法 - 检查需要的权限是否声明 - 检查运行时权限申请流程六、App Native CrashApp 通过 JNI 调用的 Native 代码崩溃时处理流程与纯 Native 进程类似6.1 识别 App Native Crashlogcat 特征 05-01 12:00:00.000 12345 12345 F libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 12345 (com.example.app) 05-01 12:00:00.500 12345 12345 F DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 05-01 12:00:00.500 12345 12345 F DEBUG: pid: 12345, tid: 12345, name: com.example.app com.example.app 05-01 12:00:00.500 12345 12345 F DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x06.2 Tombstone 中识别 App在 tombstone 中通过以下字段区分是系统进程还是 Apppid: 12345, tid: 12345, name: com.example.app com.example.app App 的进程名格式通常是包名如com.example.app与系统进程如surfaceflinger、mediaserver的命名风格不同。6.3 App Native Crash 的特点也会生成 tombstone 文件AMS 的处理依赖于 KillApplicationHandler 能否正常执行可能来不及与 Java Crash 一样会弹出 FC 对话框如果 AMS 来得及处理七、第三方崩溃收集原理很多 App 会集成崩溃收集 SDK如 Bugly、Firebase Crashlytics其原理是利用UncaughtExceptionHandler的替换机制// 典型的三方崩溃收集原理publicclassCrashHandlerimplementsThread.UncaughtExceptionHandler{privateThread.UncaughtExceptionHandlermDefaultHandler;publicvoidinit(Contextcontext){// 1. 保存系统默认 handlermDefaultHandlerThread.getDefaultUncaughtExceptionHandler();// 2. 替换为自己的 handlerThread.setDefaultUncaughtExceptionHandler(this);}OverridepublicvoiduncaughtException(Threadt,Throwablee){// 3. 先收集崩溃信息上传到服务器collectCrashInfo(e);uploadToServer();// 4. 再交给系统默认 handler 处理// 不调用这行 FC 对话框不弹出但进程也不会被系统杀// 需要自己 Process.killProcess()if(mDefaultHandler!null){mDefaultHandler.uncaughtException(t,e);}else{Process.killProcess(Process.myPid());}}}关键注意事项必须在mDefaultHandler.uncaughtException()之前完成信息收集和上传上传操作必须是同步的因为进程即将死亡如果 handler 本身抛异常会导致系统 handler 不被调用八、实战定位流程1. 确认崩溃类型 → logcat 搜索 FATAL EXCEPTION → 或 Fatal signalNative crash 2. 提取崩溃信息 → 异常类名 消息 → 崩溃线程名main? 子线程? → 堆栈顶部的 App 代码行 3. DropBox 补充信息 → dumpsys dropbox data_app_crash --print 4. 混淆还原如果必要 → retrace mapping.txt stacktrace.txt 5. 分析根因 → 空指针追溯变量的初始化和赋值 → 类型转换检查实际的运行时类型 → 数组越界检查索引计算逻辑 → 权限问题确认 AndroidManifest 和运行时权限 6. 修复验证 → 编写单元测试覆盖崩溃路径 → 在相同条件下复现验证九、总结KillApplicationHandler 是所有 Java 崩溃的统一出口异常到达线程顶层 → handler 收集信息 → 通知 AMS → 杀死进程。FC 对话框由系统端控制App 只负责死亡AMS 负责弹框和崩溃计数。crashCount 保护连续崩溃超过 2 次后不再弹框避免骚扰用户。混淆堆栈需要 mapping.txt 还原集成发布流程中务必保留 mapping 文件。App Native Crash 同样生成 tombstone但 App 开发者通常直接用 logcat 定位tombstone 作为补充。三方崩溃 SDK 通过替换 UncaughtExceptionHandler 实现先收集后转交确保系统默认流程不被中断。本文基于 AOSP 7Android Nougat源码编写。下一篇将介绍系统追踪工具——Trace 机制与性能诊断。

相关新闻

2026届计算机学生备战指南:从基础到项目实战的完整规划

2026届计算机学生备战指南:从基础到项目实战的完整规划

这类话题每年毕业季都会被翻出来,但2026届的计算机同学,现在开始准备其实已经不算早了。最核心的问题不是“如何找到工作”,而是“如何在毕业前,让自己具备被市场快速识别和接纳的能力”。这背后是一套从认知、规划到执行落地的系…

2026/9/21 8:47:22 阅读更多 →
如何免费实现多视频同步播放?GridPlayer终极指南帮你轻松搞定![特殊字符]

如何免费实现多视频同步播放?GridPlayer终极指南帮你轻松搞定![特殊字符]

如何免费实现多视频同步播放?GridPlayer终极指南帮你轻松搞定!🚀 【免费下载链接】gridplayer Play videos side-by-side 项目地址: https://gitcode.com/gh_mirrors/gr/gridplayer 你是否需要同时观看多个视频进行对比分析&#xff1…

2026/9/19 21:06:46 阅读更多 →
AWS为DynamoDB添原生向量搜索:简化AI开发,降本提效!

AWS为DynamoDB添原生向量搜索:简化AI开发,降本提效!

AWS为DynamoDB添加原生向量搜索功能:简化AI开发,降低成本并提升管理效率! 分析师表示,此次更新有望通过消除维护单独向量数据库的需求,简化AI应用开发,降低运营成本并提升管理效率。 AWS终于为其托管式NoSQ…

2026/9/21 9:59:59 阅读更多 →

最新新闻

内轮差新手必坑,面试必问的3个逻辑陷阱

内轮差新手必坑,面试必问的3个逻辑陷阱

内轮差新手必坑,面试必问的3个逻辑陷阱 官方文档里关于“内轮差”的定义通常只有一行字,但背后藏着三个让新手在面试中直接挂掉的逻辑陷阱。很多人以为这只是个数学计算题,结果一上手代码,边界条件处理得一塌糊涂。这确实是 面试必问…

2026/9/22 17:26:46 阅读更多 →
Python shuffling源码拆解速查手册:告别版本升级API变更

Python shuffling源码拆解速查手册:告别版本升级API变更

Python shuffling源码拆解速查手册:告别版本升级API变更 版本升级后 API 全变了?别慌,这份 shuffling 源码解析速查手册能帮你稳住心态。…

2026/9/22 17:26:46 阅读更多 →
爱奇艺视频下载实战项目面试突击

爱奇艺视频下载实战项目面试突击

爱奇艺视频下载实战项目面试突击 面试被问原理答不上来?别慌,很多开发者在做爱奇艺视频下载实战项目时,只关注了结果,却忽略了底层逻辑。当面试官追问“为什么不能直接抓取URL”,你如果只能回答“因为加密”,那就出局了。这不仅仅是技术细节,更是考…

2026/9/22 17:26:46 阅读更多 →
3个真实案例带你搞懂中国民族证券避坑指南

3个真实案例带你搞懂中国民族证券避坑指南

3个真实案例带你搞懂中国民族证券避坑指南 看了一堆教程还是不会写项目?别慌,这不是你的错,是没人告诉你那些“隐形”的坑。在金融数据分析和量化交易入门阶段,很多学员卡在第一步:不知道如何合规地获取和处理像 中国民族证券 这样的机构数据。…

2026/9/22 17:26:46 阅读更多 →
奔腾g3260老机复活,一文搞懂Python环境搭建避坑

奔腾g3260老机复活,一文搞懂Python环境搭建避坑

奔腾g3260老机复活,一文搞懂Python环境搭建避坑 配置环境就卡半天,甚至直接蓝屏死机,这是很多拿奔腾G3260老电脑做开发或学习的人遇到的噩梦。别急,今天咱们不聊虚的,直接上干货。…

2026/9/22 17:26:46 阅读更多 →
3个核心逻辑手写实现:彻底搞懂原汁机和榨汁机的区别

3个核心逻辑手写实现:彻底搞懂原汁机和榨汁机的区别

3个核心逻辑手写实现:彻底搞懂原汁机和榨汁机的区别 刚学会写 for 循环和 if 判断,却对着空白的 IDE 发呆,不知如何搭建一个完整的榨汁机控制程序?这是很多新手从语法入门到项目实战时最大的鸿沟。很多人以为懂原理就能干活,但真到了工程…

2026/9/22 17:25:46 阅读更多 →

日新闻

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天 配置环境就卡半天?别怪机器慢,多半是你没选对工具链。在Java、Go或Python的项目现场, 手写实现…

2026/9/22 0:00:41 阅读更多 →
剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑 面试被问原理答不上来,是不是常态?别慌。很多开发者对着 GitHub 开源仓库里的代码发呆,看似简单实则暗藏玄机。今天这份【剑帝加点】速查手册,直接带你拆解核心实现,把面试必考的原理讲透。…

2026/9/22 0:00:41 阅读更多 →
手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优 复制来的代码跑不通不知道怎么调?别慌,这种“复制粘贴地狱”在开发圈太常见了。尤其是做 图片压缩网站…

2026/9/22 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/22 4:32:41 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/22 4:38:57 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/22 8:51:04 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/21 15:36:51 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/21 15:36:51 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/22 2:43:42 阅读更多 →