Android Crash 笔记整理
内容摘抄自:《Android 开发艺术探索》
Crash 即奔溃,一般是由于程序发生了异常,却没有捕获而导致的(即用 try-catch 语句捕获),crash 时,系统会 kill 掉对应的正在运行的程序,导致闪退或者提示用户程序已经停止运行的现象。
Android 提供了关于处理该问题的方法,即 Thread.setDefaultUncaughtExceptionHandler()
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
defaultUncaughtExceptionHandler = eh;
}
public interface UncaughtExceptionHandler {
/**
* Method invoked when the given thread terminates due to the
* given uncaught exception.
* <p>Any exception thrown by this method will be ignored by the
* Java Virtual Machine.
* @param t the thread
* @param e the exception
*/
void uncaughtException(Thread t, Throwable e);
}
当发生 crash 的时候,系统会回调 UncaughtExceptionHandler 接口的 uncaughtException()
方法,从而在该方法中收集异常信息,保存在本地,方便查看,也可以上传到服务器,供开发人员分析,同时还可以在 crash 发生时,弹出对话框提醒用户,再退出应用,提升交互体验。
获取应用 crash 信息的简单实现:
1、实现自定义的 UncaughtExceptionHandler 类型对象,在其 uncaughtException()
方法中获取异常信息并进行处理(保存到本地,上传到服务器);
2、然后调用 Thread.setDefaultUncaughtExceptionHandler()
将其设置为线程默认的异常处理器(因为对应的变量为 Thread 的静态变量,因此它的作用是当前进程的所有线程)
具体的代码(kotlin 实现):
(1)先实现 UncaughtExceptionHandler 接口
// Thread.UncaughtExceptionHandler 是一个接口
// 以单例模式实现
object CrashHandler : Thread.UncaughtExceptionHandler {
private const val TAG = "CreashHandler"
var DEBUG = true
private var mDefaultCrashHandler: Thread.UncaughtExceptionHandler? = null
private const val PATH = "storage/emulated/0/Crash/log/"
private const val FILE_NAME = "crash"
private const val FILE_NAME_SUFFIX = ".trace"
private var context: Context? = null
fun init(context: Context) {
Log.d("$TAG", "CrashHandler PATH = $PATH")
this.context = context.applicationContext
// 先获取系统默认的异常处理器,通过默认的处理器去处理发生的异常
mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler()
// 然后设置成自定义的
Thread.setDefaultUncaughtExceptionHandler(this)
}
// 当程序存在为捕获的异常时,系统将会自动调用该回调方法
override fun uncaughtException(t: Thread?, e: Throwable?) {
try {
if (e == null) {
return
}
dumpExceptionToSDCard(e)
uploadToServer()
} catch (e: Exception) {
Log.d(TAG, "dump crash info failed 1 \n" + e.printStackTrace())
}
// 打印异常
e?.printStackTrace()
// 如果系统存在默认的异常处理器,则交由系统去结束程序,否则手动结束应用进程
if (mDefaultCrashHandler != null) {
mDefaultCrashHandler!!.uncaughtException(t, e)
} else {
Process.killProcess(Process.myPid())
}
}
private fun dumpExceptionToSDCard(ex: Throwable) {
if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED) {
if (DEBUG) {
return
}
}
val dir = File(PATH)
if (!dir.exists()) {
dir.mkdirs()
}
val cur = System.currentTimeMillis()
val time = SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date(cur))
val fileName = PATH + (FILE_NAME + time + FILE_NAME_SUFFIX)
Log.d(TAG, "fileName = $fileName")
val file = File(fileName)
try {
val pw = PrintWriter(BufferedWriter(FileWriter(file)))
pw.println(time)
// 省略了打印手机信息的代码
pw.println()
ex.printStackTrace(pw)
pw.close()
} catch (e: Exception) {
Log.d(TAG, "dump crash info failed 2 \n" + e.printStackTrace())
}
}
private fun uploadToServer() {
// TODO upload the info to Server
}
}
然后在 Application 初始化:
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
CrashHandler.init(this)
}
}
需要注意:
1、记得在 Manifest 文件中注册 Application
2、因为要对内存卡进行读写,因此需要先获取相关的权限
测试的时候,可以手动抛出一个异常,或者书写错误的代码来导致异常,且不进行捕获:
// 触发数组越界异常
val array = emptyArray<Int>()
array[1]
// 手动抛出一个异常
throw RuntimeException("测试")
推荐阅读
-
Android学习笔记(Android Studio) 4-2-1~2 Fragment详解(一、二)(不可不会的Activity和Fragment)
-
Android学习笔记(Android Studio) 4-1-2 Activity的生命周期(不可不会的Activity和Fragment)
-
Android学习笔记(Android Studio) 7-1 SharedPreferences 轻量数据存储(数据存储)
-
JavaScript学习笔记整理_简单实现枚举类型,扑克牌应用
-
韩顺平_PHP软件工程师玩转算法公开课(第一季)01_算法重要性_五子棋算法_汉诺塔_回溯算法_学习笔记_源代码图解_PPT文档整理
-
笔记本电脑电池无法充满电的可能原因及解决方法整理
-
Linux内核设备驱动之字符设备驱动笔记整理
-
Android NDK 开发Jni 遇到Fatal 崩溃错误后,怎么定位crash的位置
-
韩顺平_PHP软件工程师玩转算法公开课(第一季)01_算法重要性_五子棋算法_汉诺塔_回溯算法_学习笔记_源代码图解_PPT文档整理
-
PHP中常用的数组操作方法笔记整理