软件编程
位置:首页>> 软件编程>> Android编程>> Android使用CrashHandler来获取应用的crash信息的方法

Android使用CrashHandler来获取应用的crash信息的方法

作者:QDJdeveloper  发布时间:2023-07-25 20:27:38 

标签:Android,crash,信息

在日常开发的过程中应该不可避免的会发生 crash,无论你的程序写的多么完美,都不可能完全避免 crash 的发生,可能是由于 Android 底层的 bug,也可能是由于不充分的机型适配或者是糟糕的网络状况。当 crash 发生时,系统就会kill掉正在执行的程序,现象就是闪退,或者提醒用户程序已经停止运行,这对用户来说是很不友好的,也是我们不愿意看到的,更早的是当用户发生 crash,我们开发者却无法得知程序为何 crash,即便我们想去解决这个 bug,但是由于无法知道用户当时的 crash 信息,所以往往也无能为力,幸运的是,Andorid 提供了处理这类问题的方法,接下来我们就来一起看看到底 Android 给我们提供了什么方法来解决这个棘手的问题

一、Thread 类中的 setDefaultUncaughtExceptionHandler


/**
* Sets the default uncaught exception handler. This handler is invoked in
* case any Thread dies due to an unhandled exception.
*
* @param handler
*      The handler to set or null.
*/  
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler handler) {  
 Thread.defaultUncaughtHandler = handler;  
}

这个方法其实就可以解决我们应用程序的 crash 问题,设置系统默认异常处理器,当系统发生crash 时,系统就会回调 UncaughtExceptionHandler 的 uncaughtException 方法,在 uncaughtException 方法中就可以获取到异常信息,可以选择把异常信息存储下来,存储方式大家可以自己选择,然后在合适的时候通过网络将 crash 信息上传到服务器上,这样我们开发人员就可以分析用户 crash 的场景从而在后面的版本中进行修复,我们还可以在发生 crash 发生时弹出一个对话框,告诉用户程序 crash 了,然后再退出

二、实现自己的异常捕获类

1)建立异常 Handler,命名为 CrashHandler,代码如下


/**
* 异常捕获类
* Created by qiudengjiao on 2017/9/29.
*/

public class CrashHandler implements Thread.UncaughtExceptionHandler {

private static final String TAG = "CrashHandler";
 private static final boolean DEBUG = true;

private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/ryg_test/log/";
 private static final String FILE_NAME = "crash";

//log文件的后缀名
 private static final String FILE_NAME_SUFFIX = ".trace";

private static CrashHandler sInstance = new CrashHandler();

//系统默认的异常处理(默认情况下,系统会终止当前的异常程序)
 private Thread.UncaughtExceptionHandler mDefaultCrashHandler;

private Context mContext;

//构造方法私有,防止外部构造多个实例
 private CrashHandler() {
 }

public static CrashHandler getInstance() {
   return sInstance;
 }

/**
  * 初始化
  *
  * @param context
  */

public void init(Context context) {
   //获取系统默认的异常处理器
   mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
   //将当前实例设为系统默认的异常处理器
   Thread.setDefaultUncaughtExceptionHandler(this);
   //获取Context,方便内部使用
   mContext = context.getApplicationContext();
 }

/**
  * 这个是最关键的函数,当程序中有未被捕获的异常,系统将会自动调用#uncaughtException方法
  * thread为出现未捕获异常的线程,ex为未捕获的异常,有了这个throwable,我们就可以得到异常信息
  *
  * @param thread
  * @param throwable
  */
 @Override
 public void uncaughtException(Thread thread, Throwable throwable) {
   try {
     //导出异常信息到SD卡中
     dumpExceptionToSDCard(throwable);
     //这里可以通过网络上传异常信息到服务器,便于开发人员分析日志从而解决bug
     uploadExceptionToServer();
   } catch (IOException e) {
     e.printStackTrace();
   }

//打印出当前调用栈信息
   throwable.printStackTrace();

//如果系统提供了默认的异常处理器,则交给系统去结束我们的程序,否则就由我们自己结束自己
   if (mDefaultCrashHandler != null) {
     mDefaultCrashHandler.uncaughtException(thread, throwable);
   } else {
     android.os.Process.killProcess(android.os.Process.myPid());
   }
 }

/**
  * 保存到内存卡
  * 这里我们也可以根据项目需要选择其他的保存方式
  *
  * @param throwable
  * @throws IOException
  */
 private void dumpExceptionToSDCard(Throwable throwable) throws IOException {
   //如果SD卡不存在或无法使用,则无法把异常信息写入SD卡
   if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
     if (DEBUG) {
       Log.w(TAG, "sdcard unmounted,skip dump exception");
       return;
     }
   }

File dir = new File(PATH);
   if (!dir.exists()) {
     dir.mkdirs();
   }
   long current = System.currentTimeMillis();
   String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(current));
   //以当前时间创建log文件
   File file = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX);

try {
     PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
     //导出发生异常的时间
     pw.println(time);

//导出手机信息
     dumpPhoneInfo(pw);

pw.println();
     //导出异常的调用栈信息
     throwable.printStackTrace(pw);

pw.close();
   } catch (Exception e) {
     Log.e(TAG, "dump crash info failed");
   }
 }

/**
  * 收集设备参数信息
  *
  * @param pw
  * @throws PackageManager.NameNotFoundException
  */
 private void dumpPhoneInfo(PrintWriter pw) throws PackageManager.NameNotFoundException {
   //应用的版本名称和版本号
   PackageManager pm = mContext.getPackageManager();
   PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
   pw.print("App Version: ");
   pw.print(pi.versionName);
   pw.print('_');
   pw.println(pi.versionCode);

//android版本号
   pw.print("OS Version: ");
   pw.print(Build.VERSION.RELEASE);
   pw.print("_");
   pw.println(Build.VERSION.SDK_INT);

//手机制造商
   pw.print("Vendor: ");
   pw.println(Build.MANUFACTURER);

//手机型号
   pw.print("Model: ");
   pw.println(Build.MODEL);

//cpu架构
   pw.print("CPU ABI: ");
   pw.println(Build.CPU_ABI);
 }

/**
  * 将异常信息上传到服务器
  */
 private void uploadExceptionToServer() {
   //在这里写上传到服务器的逻辑
 }
}

从上面的代码可以看出,当应用程序崩溃时,CrashHandler 类会将异常信息以及设备信息写入 SD 卡,这里大家也可以根据自己项目需要进行处理,例如也可以存储在数据库中,接着将异常交给系统处理,系统会帮我们中止程序,如果系统没有默认的异常处理机制,那么就自行中止,当然而又可以选择将异常信息上传到服务器,这里我们没有实现这个逻辑,实际开发中都需要将异常信息上传到服务器

三、如何使用 CrashHandler

其实使用 CrashHandler 也非常简单,我们可以在 Application 初始化的时候来设置 CrashHandler,如下所示:


/**
* 自定义 Application 类
* Created by qiudengjiao on 2017/9/29.
*/

public class App extends Application {
 @Override
 public void onCreate() {
   super.onCreate();
   init();
 }

private void init() {
   //初始化异常捕获类 CrashHandler
   CrashHandler.getInstance().init(this);
 }
}

通过上面的操作,我们的程序就能捕获到 crash 了,同时还能从服务器上查看用户的 crash 信息,今天就写到这里,给大家推荐一本不错的书:Android 开发艺术,作者是任玉刚,相信大家也都知道,这本书的内容还是非常不错的,值得大家一看,比较适合有一定 Android 基础的同学,马上就是国庆小长假了,祝大家国庆节愉快

来源:http://blog.csdn.net/qdjdeveloper/article/details/78130226?utm_source=tuicool&utm_medium=referral

0
投稿

猜你喜欢

  • JVM 的主要作用是什么?JVM 就是 Java Virtual Machine(Java虚拟机)的缩写,JVM 屏蔽了与具体操作系统平台相
  • 本文详述了android抽奖程序的实现方法,程序为一个抽奖大转盘代码,里面定义了很多图形方法和动画。实现主要功能的SlyderView.ja
  • 本文实例为大家分享了Java实现抢红包功能的具体代码,供大家参考,具体内容如下关键思想:1.抢红包涉及多人并发操作,需要做好同步保证多线程运
  • for循环和foreach循环其实可以算得上是从属关系的,即foreach循环是可以转化成for循环,但是for循环不一定能转换成forea
  • 一、猜想数据存放路径微信现在是老少皆宜,大街小巷都在使用,已经替代了传统的短信聊天方式了,只要涉及到聊天就肯定有隐私消息,那么本文就来讲解如
  • 本文实例为大家分享了Java实现五子棋的具体代码,供大家参考,具体内容如下任务概述:五子棋是全国智力运动会竞技项目之一,是一种两人对弈的纯策
  • 我也不知道这个叫什么,就是比如我要打开我电脑的计算机,可以直接在命令行输入“calc”就可以了。现在用让代码去执行。public stati
  • 介绍在 C/C++ 中,程序员负责对象的创建和销毁。通常程序员会忽略无用对象的销毁。由于这种疏忽,在某些时候,为了创建新对象,可能没有足够的
  • 相信不少喜欢开发的朋友都已经知道微信小程序是个什么物种了,楼主也是从小程序内测期间就开始关注,并且也写过几个已经上线的微信小程序。但是基本上
  • 文章来源:csdn 作者:wangfengsdu经常听到回调函数(callback function)这个概念, 所谓回调函数,就是指这个函
  • C语言实现矩阵运算给定一个n×n的方阵,本题要求计算该矩阵除副对角线、最后一列和最后一行以外的所有元素之和。副对角线为从矩阵的右上角至左下角
  • 在上篇文章给大家介绍了使用XSD校验Mybatis的SqlMapper配置文件的方法(1),需要的朋友可以参考下。编写好XSD文件,然后来看
  • 引言偶然在Youtube上看到一名国外安卓开发者分享了一个提升应用性能的视频,其中使用到了macro benchmark来进行性能测量,包括
  • 最近一个项目是一个基于winform的报告系统,根据一系列的查询参数计算出结果,最终生成一个格式规范的word文档,刚开始数据量不大(500
  • 网站中为了防止恶意获取验证短信、验证邮箱,都会在点击获取验证码的按钮上做个倒计时的效果,如何实现这个效果,具体内容如下效果图: 代
  • 日常开发中,判断邮箱是少不了的,这个我以**C#**为例,来写一个判断方法,正则表达式是通用的,CV就可以首先引入正则需要使用的命名空间//
  • 本文实例为大家分享了C#无损高质量压缩图片的具体代码,供大家参考,具体内容如下/// 无损压缩图片  /// <param
  • 重新指定分配默认值的参数时,可以显式地为指定参数名称赋值,隐式指定的时候,是根据方法参数的顺序,靠c#编译器的推断。 代码示例: void
  • 什么是XML?XML:可扩展标记语言。XML的作用:纯文本,兼容性强。和HTML的区别:xml: 主要用来处理、存储数据。无规定标签,可扩展
  • 实例如下:/// <summary>  /// 上传ftp服务  /// </summary>
手机版 软件编程 asp之家 www.aspxhome.com