android 崩溃日志捕获,安卓Java崩溃的捕获和日志记录
Android的两种崩溃
Android 崩溃分为 Java 崩溃和 Native崩溃两种。
Java崩溃的知识点
Java崩溃.png
Java崩溃的原因
简单来说,Java崩溃就是在Java代码中,出现了未被捕获的异常,导致应用程序异常退出。
Java异常的归类
Java的异常可分为分为可查的异常(checkedexceptions)和不可查的异常(unchecked exceptions)
常见的异常可归类为如下图:
Throwable.png
其中Error和RuntimeException是unchecked exceptions,编译器默认无法通过对其处理。其余checkedexceptions,需要我们在代码中try-catch。
崩溃的捕捉
UncaughtExceptionHandler
先来看一下这个接口的作用
/**
* Interface for handlers invoked when a Thread abruptly
* terminates due to an uncaught exception.
*
When a thread is about to terminate due to an uncaught exception
* the Java Virtual Machine will query the thread for its
* UncaughtExceptionHandler using
* {@link #getUncaughtExceptionHandler} and will invoke the handler's
* uncaughtException method, passing the thread and the
* exception as arguments.
* If a thread has not had its UncaughtExceptionHandler
* explicitly set, then its ThreadGroup object acts as its
* UncaughtExceptionHandler. If the ThreadGroup object
* has no
* special requirements for dealing with the exception, it can forward
* the invocation to the {@linkplain #getDefaultUncaughtExceptionHandler
* default uncaught exception handler}.
*
* @see #setDefaultUncaughtExceptionHandler
* @see #setUncaughtExceptionHandler
* @see ThreadGroup#uncaughtException
* @since 1.5
*/
@FunctionalInterface
public interface UncaughtExceptionHandler {
/**
* Method invoked when the given thread terminates due to the
* given uncaught exception.
*
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);
}
大致意思就是如果线程发生未处理的异常,会调用UncaughtExceptionHandler的uncaugthException方法去处理异常,如果该线程没有设置UncaughtExceptionHandler,则会去调用ThreadGroup的UncaughtExceptionHandler,若还是没有,则最终getDefaultUncaughtExceptionHandler来处理异常。
系统默认的UncaughtExceptionHandler
日常当我们应用崩溃时,会有一个默认的系统弹窗,告知我们应用崩溃,那系统的崩溃是如何定义的呢?源码如下,注释已经比较完整。
/**
* Handle application death from an uncaught exception. The framework
* catches these for the main threads, so this should only matter for
* threads created by applications. Before this method runs, the given
* instance of {@link LoggingHandler} should already have logged details
* (and if not it is run first).
*/
private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
private final LoggingHandler mLoggingHandler;
/**
* Create a new KillApplicationHandler that follows the given LoggingHandler.
* If {@link #uncaughtException(Thread, Throwable) uncaughtException} is called
* on the created instance without {@code loggingHandler} having been triggered,
* {@link LoggingHandler#uncaughtException(Thread, Throwable)
* loggingHandler.uncaughtException} will be called first.
*
* @param loggingHandler the {@link LoggingHandler} expected to have run before
* this instance's {@link #uncaughtException(Thread, Throwable) uncaughtException}
* is being called.
*/
public KillApplicationHandler(LoggingHandler loggingHandler) {
this.mLoggingHandler = Objects.requireNonNull(loggingHandler);
}
@Override
public void uncaughtException(Thread t, Throwable e) {
try {
ensureLogging(t, e);
// Don't re-enter -- avoid infinite loops if crash-reporting crashes.
if (mCrashing) return;
mCrashing = true;
// Try to end profiling. If a profiler is running at this point, and we kill the
// process (below), the in-memory buffer will be lost. So try to stop, which will
// flush the buffer. (This makes method trace profiling useful to debug crashes.)
if (ActivityThread.currentActivityThread() != null) {
ActivityThread.currentActivityThread().stopProfiling();
}
// Bring up crash dialog, wait for it to be dismissed
ActivityManager.getService().handleApplicationCrash(
mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
} catch (Throwable t2) {
if (t2 instanceof DeadObjectException) {
// System process is dead; ignore
} else {
try {
Clog_e(TAG, "Error reporting crash", t2);
} catch (Throwable t3) {
// Even Clog_e() fails! Oh well.
}
}
} finally {
// Try everything to make sure this process goes away.
Process.killProcess(Process.myPid());
System.exit(10);
}
}
该接口实现在RuntimeInit类中,并在Runtime初始化时写入设置成我们默认的异常处理类
RuntimeInit.class
protected static final void commonInit() {
if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
/*
* set handlers; these apply to all threads in the VM. Apps can replace
* the default handler, but not the pre handler.
*/
LoggingHandler loggingHandler = new LoggingHandler();
Thread.setUncaughtExceptionPreHandler(loggingHandler);
Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
......
}
自定义崩溃捕捉
到这里思路已经很清晰了,我们要做的就是自己实现一个对崩溃处理的UncaughtExceptionHandler,那么我们应该设置在哪,初始化的时机在何时。我们先来看看系统初始化用到的方法,即Thread.setDefaultUncaughtExceptionHandler()。
/**
* Set the default handler invoked when a thread abruptly terminates
* due to an uncaught exception, and no other handler has been defined
* for that thread.
*
*
Uncaught exception handling is controlled first by the thread, then
* by the thread's {@link ThreadGroup} object and finally by the default
* uncaught exception handler. If the thread does not have an explicit
* uncaught exception handler set, and the thread's thread group
* (including parent thread groups) does not specialize its
* uncaughtException method, then the default handler's
* uncaughtException method will be invoked.
*
By setting the default uncaught exception handler, an application
* can change the way in which uncaught exceptions are handled (such as
* logging to a specific device, or file) for those threads that would
* already accept whatever "default" behavior the system
* provided.
*
*
Note that the default uncaught exception handler should not usually
* defer to the thread's ThreadGroup object, as that could cause
* infinite recursion.
*
* @param eh the object to use as the default uncaught exception handler.
* If null then there is no default handler.
*
* @throws SecurityException if a security manager is present and it
* denies {@link RuntimePermission}
* ("setDefaultUncaughtExceptionHandler")
*
* @see #setUncaughtExceptionHandler
* @see #getUncaughtExceptionHandler
* @see ThreadGroup#uncaughtException
* @since 1.5
*/
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
defaultUncaughtExceptionHandler = eh;
}
这个defaultUncaughtHandler是Thread类中一个静态的成员,所以,按道理,我们为任意一个线程设置异常处理,所有的线程都应该能共用这个异常处理器。为了在ui线程中添加异常处理Handler,我推荐大家在Application中添加而不是在Activity中添加。Application标识着整个应用,在Android声明周期中是第一个启动的,早于任何的Activity、Service等。
有了以上的知识,我们就可以自己来实现一个崩溃捕捉和处理的lib啦
其实实现方法网上都大同小异,主要是对异常捕获后的处理机制不一致。一般会通过储存崩溃日志并上报这种方案去解决。这里先基础实现崩溃日志文件的存储。
一个崩溃日志应该包括的基本信息有:
崩溃原因和栈记录
日期和APP版本信息
机型信息
所以我们定义基础的异常处理器如下:
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
try {
//将崩溃信息记录到文件
dumpToFile(thread, throwable);
} catch (IOException e) {
e.printStackTrace();
}
throwable.printStackTrace();
//如果系统仍有设置默认的处理器,则调用系统默认的
if (mDefaultUncaughtExceptionHandler != null) {
mDefaultUncaughtExceptionHandler.uncaughtException(thread, throwable);
} else {
//结束进程并退出
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(10);
}
}
记录文件具体如下:
private void dumpToFile(Thread thread, Throwable ex) throws IOException {
File file = null;
PrintWriter printWriter = null;
String crashTime = dataFormat.format(new Date(System.currentTimeMillis()));
String dirPath = Utils.getCrashLogPath(mContext);
File dir = new File(dirPath);
if (!dir.exists()) {
boolean ok = dir.mkdirs();
if (!ok) {
return;
}
}
//Log文件的名字
String fileName = "Crash" + "_" + crashTime + FILE_NAME_SUFFIX;
file = new File(dir, fileName);
if (!file.exists()) {
boolean createNewFileOk = file.createNewFile();
if (!createNewFileOk) {
return;
}
}
try {
//开始写日志
printWriter = new PrintWriter(new BufferedWriter(new FileWriter(file)));
//崩溃时间
printWriter.println(crashTime);
//导出APP信息
dumpAppInfo(printWriter);
//导出手机信息
dumpPhoneInfo(printWriter);
//导出异常的调用栈信息
ex.printStackTrace(printWriter);
Log.e(TAG, "崩溃日志输入完成");
} catch (Exception e) {
Log.e(TAG, "导出信息失败");
} finally {
if (printWriter != null) {
printWriter.close();
}
}
}
好了,Java崩溃捕获大致就这样。
Demo地址:
android 崩溃日志捕获,安卓Java崩溃的捕获和日志记录相关推荐
- 解密android日志xlog,安卓开发技巧2:自定义日志工具类XLog的实现
安卓开发技巧二:自定义日志工具类XLog的实现 我们在开发过程中,打印日志是必不可少的一个调试环节,然而,直接使用系统自带的Log日志类,并不能满足我们实际项目的需求:假如我们现在在开发一款比较大的项 ...
- 利用java.util.logging.Logger输出日志
log4j提供了非常灵活而又强大的日志功能,java运行库中的日志功能反而被忽略了.其实也是挺好用的,最重要的是,用这个的话就不再需要log4j的jar文件. 由于java.util.loggi ...
- Android之用UncaughtExceptionHandler实现保存崩溃日志到sdcard目录下的文件夹
1.异常和UncaughtExceptionHandler的介绍 1).Java异常处理机制中: 如果抛出的是Exception异常的话,需要有try catch进行处理,属于可以捕获exceptio ...
- adb logcat 抓取日志_手机抓取崩溃的log日志(安卓/ios)
android闪退获取日志方法: 1下载adb工具包 (工具包自己找,adb原理https://zhuanlan.zhihu.com/p/96468249) 2.注意事项 请确保电脑上只连接了一台手机 ...
- java语言中application异常退出和线程异常崩溃的捕获方法,并且在捕获的钩子方法中进行异常处理
java语言中application异常退出和线程异常崩溃的捕获方法,并且在捕获的钩子方法中进行异常处理 参考文章: (1)java语言中application异常退出和线程异常崩溃的捕获方法,并且在 ...
- 导致Android手机崩溃的壁纸,一张壁纸导致安卓手机崩溃作者首发声:绝非故意...
原标题:一张壁纸导致安卓手机崩溃作者首发声:绝非故意 "一张壁纸就导致手机崩溃"的Bug相信许多安卓手机用户都有所耳闻,我们此前也对该Bug进行了报道,想了解详情的朋友可以点此查看 ...
- 让android手机崩溃的壁纸,一张壁纸导致安卓手机崩溃作者首发声:绝非故意
原标题:一张壁纸导致安卓手机崩溃作者首发声:绝非故意 "一张壁纸就导致手机崩溃"的Bug相信许多安卓手机用户都有所耳闻,我们此前也对该Bug进行了报道,想了解详情的朋友可以点此查看 ...
- android 获取堆栈地址,关于java native interface:如何捕获SIGSEGV(分段错误)并在Android下的JNI下获取堆栈跟踪?...
我正在将一个项目转移到新的Android本机开发工具包(即JNI)中,我想捕获sigsegv,如果它发生(也可能是sigill.sigabrt.sigfpe),以便呈现一个很好的崩溃报告对话框,而不是 ...
- Android开发高手课笔记 - 01 崩溃优化(上):关于“崩溃”那点事
Android 的两种崩溃 Java 崩溃就是在 Java 代码中,出现了未捕获的异常,导致程序异常退出 Native 崩溃一般都是因为在 Native 代码中访问非法地址,也可能是地址对齐出了问题, ...
最新文章
- 计算机文化英文15版答案,15信高《计算机文化基础》期中考试题答案
- 这是要把前几年积累的C++的节操给丢光吗
- haproxy 参数说明
- Angular - angular2升级到angular8
- php radio js,如何使用JavaScript设置radio选中的示例
- 网站图片下载 Python
- Chrome您的连接不是私密连接,解决办法
- python qq邮箱,Python使用QQ邮箱发送邮件报错smtplib.SMTPAuthenticationError
- Grub4Dos 学习笔记
- 时间差太大导致Windows时间同步无法自动更新时间
- 世界上最小的操作系统MenuetOS,仅有1.4M,安装运行全教程
- CSS实现文字动画效果
- 微信浏览器网页点击图片缩放
- 手机设备唤醒计算机,手机微信实现网络唤醒电脑(WOL),远程开机,WakeOnLan – 全栈笔记...
- 11届蓝桥杯青少年组C++全国赛高级组
- 没有这个路径C:\Program Files\Microsoft Office\root\Office16怎么办
- Paste Image插件初始化设置:修改图片保存路径、自动插入图片描述信息
- 区块链 + 边缘计算,掀开智慧医疗新篇章
- Matlab程序设计语言基础
- 归并排序怎么写,看这里( MergeSort 和 _MergeSort )