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崩溃的捕获和日志记录相关推荐

  1. 解密android日志xlog,安卓开发技巧2:自定义日志工具类XLog的实现

    安卓开发技巧二:自定义日志工具类XLog的实现 我们在开发过程中,打印日志是必不可少的一个调试环节,然而,直接使用系统自带的Log日志类,并不能满足我们实际项目的需求:假如我们现在在开发一款比较大的项 ...

  2. 利用java.util.logging.Logger输出日志

     log4j提供了非常灵活而又强大的日志功能,java运行库中的日志功能反而被忽略了.其实也是挺好用的,最重要的是,用这个的话就不再需要log4j的jar文件. 由于java.util.loggi ...

  3. Android之用UncaughtExceptionHandler实现保存崩溃日志到sdcard目录下的文件夹

    1.异常和UncaughtExceptionHandler的介绍 1).Java异常处理机制中: 如果抛出的是Exception异常的话,需要有try catch进行处理,属于可以捕获exceptio ...

  4. adb logcat 抓取日志_手机抓取崩溃的log日志(安卓/ios)

    android闪退获取日志方法: 1下载adb工具包 (工具包自己找,adb原理https://zhuanlan.zhihu.com/p/96468249) 2.注意事项 请确保电脑上只连接了一台手机 ...

  5. java语言中application异常退出和线程异常崩溃的捕获方法,并且在捕获的钩子方法中进行异常处理

    java语言中application异常退出和线程异常崩溃的捕获方法,并且在捕获的钩子方法中进行异常处理 参考文章: (1)java语言中application异常退出和线程异常崩溃的捕获方法,并且在 ...

  6. 导致Android手机崩溃的壁纸,一张壁纸导致安卓手机崩溃作者首发声:绝非故意...

    原标题:一张壁纸导致安卓手机崩溃作者首发声:绝非故意 "一张壁纸就导致手机崩溃"的Bug相信许多安卓手机用户都有所耳闻,我们此前也对该Bug进行了报道,想了解详情的朋友可以点此查看 ...

  7. 让android手机崩溃的壁纸,一张壁纸导致安卓手机崩溃作者首发声:绝非故意

    原标题:一张壁纸导致安卓手机崩溃作者首发声:绝非故意 "一张壁纸就导致手机崩溃"的Bug相信许多安卓手机用户都有所耳闻,我们此前也对该Bug进行了报道,想了解详情的朋友可以点此查看 ...

  8. android 获取堆栈地址,关于java native interface:如何捕获SIGSEGV(分段错误)并在Android下的JNI下获取堆栈跟踪?...

    我正在将一个项目转移到新的Android本机开发工具包(即JNI)中,我想捕获sigsegv,如果它发生(也可能是sigill.sigabrt.sigfpe),以便呈现一个很好的崩溃报告对话框,而不是 ...

  9. Android开发高手课笔记 - 01 崩溃优化(上):关于“崩溃”那点事

    Android 的两种崩溃 Java 崩溃就是在 Java 代码中,出现了未捕获的异常,导致程序异常退出 Native 崩溃一般都是因为在 Native 代码中访问非法地址,也可能是地址对齐出了问题, ...

最新文章

  1. 计算机文化英文15版答案,15信高《计算机文化基础》期中考试题答案
  2. 这是要把前几年积累的C++的节操给丢光吗
  3. haproxy 参数说明
  4. Angular - angular2升级到angular8
  5. php radio js,如何使用JavaScript设置radio选中的示例
  6. 网站图片下载 Python
  7. Chrome您的连接不是私密连接,解决办法
  8. python qq邮箱,Python使用QQ邮箱发送邮件报错smtplib.SMTPAuthenticationError
  9. Grub4Dos 学习笔记
  10. 时间差太大导致Windows时间同步无法自动更新时间
  11. 世界上最小的操作系统MenuetOS,仅有1.4M,安装运行全教程
  12. CSS实现文字动画效果
  13. 微信浏览器网页点击图片缩放
  14. 手机设备唤醒计算机,手机微信实现网络唤醒电脑(WOL),远程开机,WakeOnLan – 全栈笔记...
  15. 11届蓝桥杯青少年组C++全国赛高级组
  16. 没有这个路径C:\Program Files\Microsoft Office\root\Office16怎么办
  17. Paste Image插件初始化设置:修改图片保存路径、自动插入图片描述信息
  18. 区块链 + 边缘计算,掀开智慧医疗新篇章
  19. Matlab程序设计语言基础
  20. 归并排序怎么写,看这里( MergeSort 和 _MergeSort )

热门文章

  1. Tensorflow 踩的坑(一)
  2. 董事、执行董事、总裁、总经理
  3. C#获取客户端IP地址
  4. UVA 818 Cutting Chains 切断圆环链 (暴力dfs)
  5. h2database源码浅析:TransactionMap、MVMap、MVStore
  6. Web 设计与开发终极资源大全
  7. 帆软报表判断传入条件是否为空,根据逗号分隔
  8. bzoj1577 [Usaco2009 Feb]庙会捷运Fair Shuttle
  9. 牛客网NOIP赛前集训营 第6场 T1 最长路
  10. 【PAT】B1004 成绩排名