程序崩溃是应用迭代中不可避免的问题,即使有着5年或者10年经验的程序猿也无法完全保证自己的代码没有任何的bug导致崩溃,现在有一些第三方平台可以帮助我们搜集应用程序的崩溃,比如友盟,详情如下图

虽然能够看到崩溃的日志以及机型等,但还是不是很方便,如果需要精确定位的话需要用户提供崩溃的时间点、机型等信息,所以最好的办法就是我们把崩溃的信息保存在用户的sd卡上,必要的时候发送到后台或者让用户手动提供一下文件,下面就来看如何实现这个功能。

Android 系统提供了处理这类问题的方法,Thread 类中提供了一个方法 setDefaultUncaughtExceptionHandler,设置了这个默认的异常处理器之后当程序发生异常之后就会回调uncaugthException()这个方法,然后可以在这个回调里面捕获异常信息,保存到文件。

话不多说,直接上代码:

  1.  
  1. package com.hxc.supreme.utils;
  2.  
  3. import android.content.Context;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.os.Build;
    import android.os.Environment;
    import android.os.Looper;
    import android.util.Log;
    import android.widget.Toast;
  4.  
  5. import com.hxc.supreme.MainApplication;
  6.  
  7. import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.io.StringWriter;
    import java.io.Writer;
    import java.lang.reflect.Field;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
  8.  
  9. import static com.hxc.supreme.BuildConfig.DEBUG;
  10.  
  11. /**
    * UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.
    * 在Application 中调用
    * CrashHandlerUtil.getInstance().init(this);
    */
    public class XCrashHandlerUtils implements Thread.UncaughtExceptionHandler {
    private static final String TAG = "XCrashHandlerUtils";
    //系统默认的UncaughtException处理类
    private Thread.UncaughtExceptionHandler mDefaultHandler;
    //CrashHandler实例
    private static XCrashHandlerUtils INSTANCE = new XCrashHandlerUtils();
    //程序的Context对象
    private Context mContext;
    //用来存储设备信息和异常信息
    private String crashTip = "似乎遇到了一点小麻烦,程序即将重新启动";
    /**
    * 文件名
    */
    public static final String FILE_NAME = "crash";
    /**
    * 异常日志 存储位置为根目录下的 Crash文件夹
    */
    private static final String PATH = Environment.getExternalStorageDirectory().getPath() +
    "/Supreme_crash/";
    /**
    * 文件名后缀
    */
    private static final String FILE_NAME_SUFFIX = ".txt";
  12.  
  13. public String getCrashTip() {
    return crashTip;
    }
  14.  
  15. public void setCrashTip(String crashTip) {
    this.crashTip = crashTip;
    }
  16.  
  17. /**
    * 保证只有一个CrashHandler实例
    */
    private XCrashHandlerUtils() {
    }
  18.  
  19. /**
    * 获取CrashHandler实例 ,单例模式
    *
    * @return 单例
    */
    public static XCrashHandlerUtils getInstance() {
    return INSTANCE;
    }
  20.  
  21. /**
    * 初始化
    *
    * @param context 上下文
    */
    public void init(Context context) {
    mContext = context;
    //获取系统默认的UncaughtException处理器
    mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
    //设置该CrashHandler为程序的默认处理器
    Thread.setDefaultUncaughtExceptionHandler(this);
    }
  22.  
  23. /**
    * 这个是最关键的函数,当系统中有未被捕获的异常,系统将会自动调用 uncaughtException 方法
    *
    * @param thread 为出现未捕获异常的线程
    * @param ex 为未捕获的异常 ,可以通过e 拿到异常信息
    */
    @Override
    public void uncaughtException(Thread thread, final Throwable ex) {
    //导入异常信息到SD卡中
    try {
    dumpExceptionToSDCard(ex);
    } catch (IOException e) {
    e.printStackTrace();
    }
    //这里可以上传异常信息到服务器,便于开发人员分析日志从而解决Bug
    // uploadExceptionToServer();
    ex.printStackTrace();
    //如果系统提供了默认的异常处理器,则交给系统去结束程序,否则就由自己结束自己
    if (mDefaultHandler != null) {
    mDefaultHandler.uncaughtException(thread, ex);
    } else {
    android.os.Process.killProcess(android.os.Process.myPid());
    System.exit(1);
    }
  24.  
  25. }
  26.  
  27. /**
    * 将异常信息写入SD卡
    *
    * @param e
    */
    private void dumpExceptionToSDCard(Throwable e) 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));
    //在定义的Crash文件夹下创建文件
    File file = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX);
  28.  
  29. try {
    PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
    //写入时间
    pw.println(time);
    //写入手机信息
    dumpPhoneInfo(pw);
    pw.println();//换行
    e.printStackTrace(pw);
    pw.close();//关闭输入流
    } catch (Exception e1) {
    Log.e(TAG, "dump crash info failed");
    }
  30.  
  31. }
  32.  
  33. /**
    * 获取手机各项信息
    *
    * @param pw
    */
    private void dumpPhoneInfo(PrintWriter pw) throws PackageManager.NameNotFoundException {
    //得到包管理器
    PackageManager pm = mContext.getPackageManager();
    //得到包对象
    PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
    //写入APP版本号
    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: ");
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    pw.println(Build.SUPPORTED_ABIS);
    } else {
    pw.println(Build.CPU_ABI);
    }
    }
  34.  
  35. }
  1.  

然后再Mainapplication的onCreate中调用一下XCrashHandlerUtils.init()方法,接下来写一个bug看一下效果,代码很简单

  1. package com.hxc.supreme.activity;
  2.  
  3. import android.content.ComponentName;
  4. import android.content.Intent;
  5. import android.content.ServiceConnection;
  6. import android.graphics.Color;
  7. import android.os.Bundle;
  8. import android.os.IBinder;
  9. import android.support.annotation.Nullable;
  10. import android.support.v7.app.AppCompatActivity;
  11. import android.view.Gravity;
  12. import android.view.LayoutInflater;
  13. import android.view.View;
  14. import android.view.ViewGroup;
  15. import android.widget.Button;
  16. import android.widget.LinearLayout;
  17. import android.widget.TextView;
  18.  
  19. import com.hxc.supreme.R;
  20. import com.hxc.supreme.service.MainService;
  21.  
  22. /**
  23. * created by huxc on 2017/9/28.
  24. * func: ViewsTestActivity
  25. * email: hxc242313@qq.com
  26. */
  27.  
  28. public class ViewsTestActivity extends AppCompatActivity implements View.OnClickListener {
  29. private LinearLayout mainLayout;
  30.  
  31. @Override
  32. protected void onCreate(@Nullable Bundle savedInstanceState) {
  33. super.onCreate(savedInstanceState);
  34. setContentView(R.layout.activity_views_test);
  35. // mainLayout = findViewById(R.id.layout_main);
  36.  
  37. View view = LayoutInflater.from(this).inflate(R.layout.button_view, null);
  38. TextView textView = new TextView(this);
  39. textView.setText("add View Dynamic");
  40. textView.setGravity(Gravity.CENTER);
  41. textView.setAllCaps(false);
  42. textView.setTextColor(Color.RED);
  43. mainLayout.addView(textView,new LinearLayout.LayoutParams(300, 200));
  44. }
  45.  
  46. @Override
  47. protected void onResume() {
  48. super.onResume();
  49. }
  50.  
  51. @Override
  52. public void onClick(View view) {
  53. switch (view.getId()) {
  54. }
  55. }
  56.  
  57. }

第36行把findviewById屏蔽了,然后运行了一下程序,看一下logcat中的崩溃信息:

crash文件的保存路径是Supreme_crash,看一下文件中的内容:

和控制台输出的一毛一样,而且还打印出了手机的型号,app的版本等相关信息,大功告成!

Android 应用程序崩溃日志捕捉的更多相关文章

  1. 捕android程序崩溃日志

    主要类别: package com.example.callstatus; import java.io.File; import java.io.FileOutputStream; import j ...

  2. Android平台程序崩溃的类型及原因列举

    Android平台程序崩溃大家都应该遇到过,force close和ANR应该是大家遇到较多的. 这里把Android平台程序崩溃的各种类型做一个简述和原因列举. 1.ANR(可见ANR): 发生场景 ...

  3. iOS - 捕获应用程序崩溃日志

    作为一名iOS移动应用开发者,为了确保你的应用程序正确无误,在将应用程序提交到应用商店之前,你必定会进行大量的测试工作:而且在你测试的过程中应用程序运行的很好,但是在应用商店上线之后,还是有用户抱怨应 ...

  4. iOS 中捕获程序崩溃日志

    iOS 中捕获程序崩溃日志 (2014-04-22 17:35:59) 转载▼     iOS开发中遇到程序崩溃是很正常的事情,如何在程序崩溃时捕获到异常信息并通知开发者,是大多数软件都选择的方法.下 ...

  5. Android将程序崩溃信息保存本地文件

    大家都知道,现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象,开发者个人不可能购买所有设备逐个调试,所以在程序发布出去之后,如果出现了 ...

  6. iOS开发-捕获程序崩溃日志

    iOS开发中遇到程序崩溃是很正常的事情,如何在程序崩溃时捕获到异常信息并通知开发者,是大多数软件都选择的方法.下面就介绍如何在iOS中实现: 1. 在程序启动时加上一个异常捕获监听,用来处理程序崩溃时 ...

  7. Android在程序崩溃或者捕获异常之后重新启动app

    在Android应用开发中,偶尔会因为测试的不充分导致一些异常没有被捕获,这时应用会出现异常并强制关闭,这样会导致很不好的用户体验,为了解决这个问题,我们需要捕获相关的异常并做处理. 首先捕获程序崩溃 ...

  8. iOS 中捕获程序崩溃日志 (2014-04-22 17:35:59)

    http://blog.sina.com.cn/s/blog_b71d24920101ky2d.html iOS开发中遇到程序崩溃是很正常的事情,如何在程序崩溃时捕获到异常信息并通知开发者,是大多数软 ...

  9. iOS 捕获程序崩溃日志

    iOS开发中遇到程序崩溃是很正常的事情,如何在程序崩溃时捕获到异常信息并通知开发者? 下面就介绍如何在iOS中实现: 1. 在程序启动时加上一个异常捕获监听,用来处理程序崩溃时的回调动作 NSSetU ...

随机推荐

  1. compose函数

    compose函数 在学习redux源码的时候看到了其中的工具函数compose,compose函数的作用就是组合函数,依次组合传入的函数: 后一个函数作为前一个函数的参数 最后一个函数可以接受多个参 ...

  2. jquery.jtable的事件

    前景提要最近在使用abp zero框架帮朋友搭建一个工厂管理系统.其中有一块功能的话是通过定时爬虫拉取当日的铝价.铝价展示用的是abp zero框架中土牛写的jquery.jtable,铝价需要根据当 ...

  3. Python编程Day4——if判断、while循环、for循环

    一.if判断 语法一: if条件: 代码块1 代码块2 代码块3 示例: sex='female' age=18 is_beautiful=True if sex =='female'and age& ...

  4. 在 Windows 上可以用 Docker 吗?

    作者:陈计节 个人博客:https://blog.jijiechen.com/post/docker-on-windows/ Docker,或者准确一点说,容器技术,在近几年里几乎成为了应用分发和集群 ...

  5. 5-15 bootcss 之 modal 以及 jquery ui 之datepicker 小记

    最近公司在用bootstrap和Jquery UI做项目,类似与OA的东西前两天碰到点问题,记录一下.希望读者不要在遇到和我一样的问题. 1 datepicker.不知道怎么自己下载的bootcss里 ...

  6. IDEA的几个常用配置,日常开发必备。

    用了IDEA有很长时间了,身边的同事朋友也都慢慢的开始都从Eclipse切换到IDEA了,其实无论是Eclipse还是IntelliJ IDEA都是开发工具而已,各自都有优点.但是刚从Eclipse切 ...

  7. netty源码解解析(4.0)-14 Channel NIO实现:读取数据

     本章分析Nio Channel的数据读取功能的实现. Channel读取数据需要Channel和ChannelHandler配合使用,netty设计数据读取功能包括三个要素:Channel, Eve ...

  8. Perl一行式:处理空白符号

    perl一行式程序系列文章:Perl一行式 假如文件file.log内容如下: root x 0 0 root /root /bin/bash daemon x 1 1 daemon /usr/sbi ...

  9. HBuilder + PHP开发环境配置

      HBuilder 集成开发环境简介 HBuilder是DCloud(数字天堂)推出的一款支持HTML5的Web开发IDE.HBuilder的编写用到了Java.C.Web和Ruby.HBuilde ...

  10. element-ui el-input只显示下划线

    只需要增加样式 .el-input__inner { width: 220px; border-top-width: 0px; border-left-width: 0px; border-right ...