在Android开发中,应用的稳定性是衡量产品质量的核心指标之一。用户最无法容忍的体验莫过于正在使用的应用突然“闪退”或弹出“ unfortunately, app has stopped”的对话框。这不仅会导致用户流失,还会直接拉低App在用户心中的评分。
本文将从异常捕获机制、崩溃原因分析以及防御性编程策略三个维度,深入探讨如何解决APP闪退崩溃问题。
有关于异常捕获的视频教程请参考:https://www.bilibili.com/video/BV1Bu4y147DG?p=4
一、 核心机制:如何捕获未捕获的异常
Android系统基于Java虚拟机运行,当线程中抛出一个未被try-catch捕获的异常(Throwable)时,线程会终止。如果是主线程(UI线程)发生这种情况,就会导致应用崩溃。
要解决这个问题,首先需要“抓住”它(产生异常的原因),轻语言安卓开发提供了3种方式捕获异常原因。
1. 使用异常捕获器组件捕获全局异常
这种方式适合捕获不清楚是什么地方的代码造成的异常,我们可以在窗口创建完毕的事件中,创建一个全局捕获器对象,然后初始化即可。当App发生闪退异常时,将会自动收集产生崩溃的原因并储存到指定文件中,通过查看文件即可了解到App产生崩溃的大致异常原因。
事件 窗口创建完毕()
变量 异常捕获器1 = 创建 异常捕获器()
' 初始化并设置储存捕获的异常的信息
异常捕获器1.初始化(取储存卡路径() + "/error.txt")
结束 事件2.使用异常捕获语句
异常捕获语句适合用于抓取产生崩溃的已知代码,例如:当点击按钮,执行某句代码或调用某个函数时,发生异常,则可以针对性的捕获这个函数产生的异常。
事件 按钮1.被单击(来源对象 为 视图)
' 提示:
' 输入 异常捕获首 按下键盘上的 TAB 键、会自动完成代码块
异常捕获首
' 这里我们模拟一个数组索引越界的异常
变量 数组1 = { 1,2,3 }
' 数组总长度为3、但获取下标为10的元素、则会引发异常
调试输出("第10个数组成员" + 数组1[10])
异常被捕获(异常信息 为 对象)
' 这里返回的 异常信息 该变量就是具体导致程序崩溃
' 的代码;可以将错误输出到标签或调试输出具体错误信息
' 注意:这里的变量名“异常信息”不可修改
标签1.标题 = "异常信息:" + 异常信息
调试输出("异常信息:" + 异常信息)
异常捕获尾
结束 事件3.使用IDE调试面板
当APP编译安装到模拟器或手机上时,在IDE底部的调试面板中开启捕获日志信息,此时操作APP,如果APP发生崩溃,则相关崩溃原因将会直接被输出到调试面板中,根据输出信息则可以大致判断产生崩溃的原因。
在线文档:App调试与闪退、异常捕获
二、 深度分析:引起崩溃闪退的常见原因
捕获到异常只是第一步,更重要的是分析堆栈信息(Stack Trace)以定位根因。以下是几类最常见的崩溃原因:
1. Java层异常
这是最常见的崩溃类型,通常由代码逻辑错误引起。
空指针异常:这是Android开发中的“头号杀手”。例如,在定义集合,但未创建对象就直接调用其定义的集合对象的函数,或者访问未初始化的对象。
数组/集合越界:在遍历列表时,索引超出了集合的范围。
类型转换异常:试图将一个对象强制转换为它不属于的类型。
资源未找到:引用了不存在的布局ID或字符串资源。
2. 内存溢出
当应用申请的内存超过了系统分配的限制(不同机型限制不同,通常为几十MB到几百MB),系统会抛出OutOfMemoryError并终止进程。
图片加载不当:一次性加载大量高清图片,或者加载的图片尺寸远超ImageView的显示尺寸。
内存泄漏:长生命周期对象持有短生命周期对象(如大文件或对象)的引用,导致内存无法回收,最终堆积至溢出。
3. 主线程阻塞与ANR
虽然ANR(Application Not Responding)不是典型的“崩溃”(Crash),但它会导致应用“假死”,最终被用户强制关闭或被系统杀死。
耗时操作在主线程:在主线程进行网络请求、复杂的数据库读写或大量的位图解码。
死锁:多线程同步时发生死锁,导致主线程无法获取锁资源。
4. Native层崩溃
随着音视频处理、游戏引擎和加密算法的普及,NDK开发越来越常见。Native层的崩溃(如SIGSEGV)通常更难调试。
野指针/空指针解引用:C++代码中访问了已释放的内存。
栈溢出:递归调用层级过深。
三、 解决方案与防御性编程
要想成为一个专业的开发者,在编写软件时,要养成未雨绸缪的习惯,在开发阶段就构建防御体系。
1. 严格的空值检查
在任何可能产生空对象的地方,都特别留意,增加其对空值的判断。
2. 异步与多线程管理
线程切换:确保耗时操作(网络、IO)在子线程执行,UI更新在主线程执行。
3. 图片加载优化
尽可能使用合适的图片,而不使用体积过大的图,避免重复,频繁加载图片。
四、 总结
解决Android APP闪退崩溃是一个系统工程。我们需要:
事前:通过防御性编程、静态代码扫描和严格的测试来预防。
事中:捕获其产生异常的原因并加以分析。
事后:优化代码细节,归档记录其产生原因和相关解决方案。