• Android捕获崩溃异常


    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。

    那么今天主要讲一下如何去捕捉系统出现的Unchecked异常。何为Unchecked异常呢,换句话说就是指非受检异常,它不能用try-catch来显示捕捉。

    我们先从Exception讲起。Exception分为两类:一种是CheckedException,一种是UncheckedException。这两种Exception的区别主要是CheckedException需要用try...catch...显示的捕获,而UncheckedException不需要捕获。通常UncheckedException又叫做RuntimeException。《effective java》指出:对于可恢复的条件使用被检查的异常(CheckedException),对于程序错误(言外之意不可恢复,大错已经酿成)使用运行时异常(RuntimeException)。我们常见的RuntimeExcepiton有IllegalArgumentException、IllegalStateException、NullPointerException、IndexOutOfBoundsException等等。对于那些CheckedException就不胜枚举了,我们在编写程序过程中try...catch...捕捉的异常都是CheckedException。io包中的IOException及其子类,这些都是CheckedException。

    一、使用UncaughtExceptionHandler来捕获unchecked异常

    UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告。

    直接上代码吧

    复制代码
      1 import java.io.File;
      2 import java.io.FileOutputStream;
      3 import java.io.PrintWriter;
      4 import java.io.StringWriter;
      5 import java.io.Writer;
      6 import java.lang.Thread.UncaughtExceptionHandler;
      7 import java.lang.reflect.Field;
      8 import java.text.DateFormat;
      9 import java.text.SimpleDateFormat;
     10 import java.util.Date;
     11 import java.util.HashMap;
     12 import java.util.Locale;
     13 import java.util.Map;
     14 import java.util.Map.Entry;
     15 import java.util.regex.Matcher;
     16 import java.util.regex.Pattern;
     17 
     18 import android.annotation.SuppressLint;
     19 import android.content.Context;
     20 import android.content.pm.PackageInfo;
     21 import android.content.pm.PackageManager;
     22 import android.content.pm.PackageManager.NameNotFoundException;
     23 import android.os.Build;
     24 import android.os.Environment;
     25 import android.os.Looper;
     26 import android.util.Log;
     27 import android.widget.Toast;
     28 
     29 /**
     30  * UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.
     31  * 
     32  * @author user
     33  * 
     34  */
     35 @SuppressLint("SdCardPath")
     36 public class CrashHandler implements UncaughtExceptionHandler {
     37 
     38     public static final String TAG = "TEST";
     39 
     40     // CrashHandler 实例
     41     private static CrashHandler INSTANCE = new CrashHandler();
     42 
     43     // 程序的 Context 对象
     44     private Context mContext;
     45 
     46     // 系统默认的 UncaughtException 处理类
     47     private Thread.UncaughtExceptionHandler mDefaultHandler;
     48 
     49     // 用来存储设备信息和异常信息
     50     private Map<String, String> infos = new HashMap<String, String>();
     51 
     52     // 用来显示Toast中的信息
     53     private static String error = "程序错误,额,不对,我应该说,服务器正在维护中,请稍后再试";
     54 
     55     private static final Map<String, String> regexMap = new HashMap<String, String>();
     56 
     57     // 用于格式化日期,作为日志文件名的一部分
     58     private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss",
     59             Locale.CHINA);
     60 
     61     /** 保证只有一个 CrashHandler 实例 */
     62     private CrashHandler() {
     63         //
     64     }
     65 
     66     /** 获取 CrashHandler 实例 ,单例模式 */
     67     public static CrashHandler getInstance() {
     68         initMap();
     69         return INSTANCE;
     70     }
     71 
     72     /**
     73      * 初始化
     74      * 
     75      * @param context
     76      */
     77     public void init(Context context) {
     78         mContext = context;
     79 
     80         // 获取系统默认的 UncaughtException 处理器
     81         mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
     82 
     83         // 设置该 CrashHandler 为程序的默认处理器
     84         Thread.setDefaultUncaughtExceptionHandler(this);
     85         Log.d("TEST", "Crash:init");
     86     }
     87 
     88     /**
     89      * 当 UncaughtException 发生时会转入该函数来处理
     90      */
     91     @Override
     92     public void uncaughtException(Thread thread, Throwable ex) {
     93         if (!handleException(ex) && mDefaultHandler != null) {
     94             // 如果用户没有处理则让系统默认的异常处理器来处理
     95             mDefaultHandler.uncaughtException(thread, ex);
     96             Log.d("TEST", "defalut");
     97         } else {
     98             try {
     99                 Thread.sleep(3000);
    100             } catch (InterruptedException e) {
    101                 Log.e(TAG, "error : ", e);
    102             }
    103             // 退出程序
    104             android.os.Process.killProcess(android.os.Process.myPid());
    105             // mDefaultHandler.uncaughtException(thread, ex);
    106             System.exit(1);
    107         }
    108     }
    109 
    110     /**
    111      * 自定义错误处理,收集错误信息,发送错误报告等操作均在此完成
    112      * 
    113      * @param ex
    114      * @return true:如果处理了该异常信息;否则返回 false
    115      */
    116     private boolean handleException(Throwable ex) {
    117         if (ex == null) {
    118             return false;
    119         }
    120 
    121         // 收集设备参数信息
    122         // collectDeviceInfo(mContext);
    123         // 保存日志文件
    124         saveCrashInfo2File(ex);
    125         // 使用 Toast 来显示异常信息
    126         new Thread() {
    127             @Override
    128             public void run() {
    129                 Looper.prepare();
    130                 Toast.makeText(mContext, error, Toast.LENGTH_LONG).show();
    131                 Looper.loop();
    132             }
    133         }.start();
    134         return true;
    135     }
    136 
    137     /**
    138      * 收集设备参数信息
    139      * 
    140      * @param ctx
    141      */
    142     public void collectDeviceInfo(Context ctx) {
    143         try {
    144             PackageManager pm = ctx.getPackageManager();
    145             PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),
    146                     PackageManager.GET_ACTIVITIES);
    147 
    148             if (pi != null) {
    149                 String versionName = pi.versionName == null ? "null"
    150                         : pi.versionName;
    151                 String versionCode = pi.versionCode + "";
    152                 infos.put("versionName", versionName);
    153                 infos.put("versionCode", versionCode);
    154             }
    155         } catch (NameNotFoundException e) {
    156             Log.e(TAG, "an error occured when collect package info", e);
    157         }
    158 
    159         Field[] fields = Build.class.getDeclaredFields();
    160         for (Field field : fields) {
    161             try {
    162                 field.setAccessible(true);
    163                 infos.put(field.getName(), field.get(null).toString());
    164                 Log.d(TAG, field.getName() + " : " + field.get(null));
    165             } catch (Exception e) {
    166                 Log.e(TAG, "an error occured when collect crash info", e);
    167             }
    168         }
    169     }
    170 
    171     /**
    172      * 保存错误信息到文件中 *
    173      * 
    174      * @param ex
    175      * @return 返回文件名称,便于将文件传送到服务器
    176      */
    177     private String saveCrashInfo2File(Throwable ex) {
    178         StringBuffer sb = getTraceInfo(ex);
    179         Writer writer = new StringWriter();
    180         PrintWriter printWriter = new PrintWriter(writer);
    181         ex.printStackTrace(printWriter);
    182         Throwable cause = ex.getCause();
    183         while (cause != null) {
    184             cause.printStackTrace(printWriter);
    185             cause = cause.getCause();
    186         }
    187         printWriter.close();
    188 
    189         String result = writer.toString();
    190         sb.append(result);
    191         try {
    192             long timestamp = System.currentTimeMillis();
    193             String time = formatter.format(new Date());
    194             String fileName = "crash-" + time + "-" + timestamp + ".log";
    195 
    196             if (Environment.getExternalStorageState().equals(
    197                     Environment.MEDIA_MOUNTED)) {
    198                 String path = Environment.getExternalStorageDirectory()
    199                         + "/crash/";
    200                 File dir = new File(path);
    201                 if (!dir.exists()) {
    202                     dir.mkdirs();
    203                 }
    204                 FileOutputStream fos = new FileOutputStream(path + fileName);
    205                 fos.write(sb.toString().getBytes());
    206                 fos.close();
    207             }
    208 
    209             return fileName;
    210         } catch (Exception e) {
    211             Log.e(TAG, "an error occured while writing file...", e);
    212         }
    213 
    214         return null;
    215     }
    216 
    217     /**
    218      * 整理异常信息
    219      * @param e
    220      * @return
    221      */
    222     public static StringBuffer getTraceInfo(Throwable e) {
    223         StringBuffer sb = new StringBuffer();
    224 
    225         Throwable ex = e.getCause() == null ? e : e.getCause();
    226         StackTraceElement[] stacks = ex.getStackTrace();
    227         for (int i = 0; i < stacks.length; i++) {
    228             if (i == 0) {
    229                 setError(ex.toString());
    230             }
    231             sb.append("class: ").append(stacks[i].getClassName())
    232                     .append("; method: ").append(stacks[i].getMethodName())
    233                     .append("; line: ").append(stacks[i].getLineNumber())
    234                     .append(";  Exception: ").append(ex.toString() + "
    ");
    235         }
    236         Log.d(TAG, sb.toString());
    237         return sb;
    238     }
    239 
    240     /**
    241      * 设置错误的提示语
    242      * @param e
    243      */
    244     public static void setError(String e) {
    245         Pattern pattern;
    246         Matcher matcher;
    247         for (Entry<String, String> m : regexMap.entrySet()) {
    248             Log.d(TAG, e+"key:" + m.getKey() + "; value:" + m.getValue());
    249             pattern = Pattern.compile(m.getKey());
    250             matcher = pattern.matcher(e);
    251             if(matcher.matches()){
    252                 error = m.getValue();
    253                 break;
    254             }
    255         }
    256     }
    257 
    258     /**
    259      * 初始化错误的提示语
    260      */
    261     private static void initMap() {
    262         // Java.lang.NullPointerException
    263         // java.lang.ClassNotFoundException
    264         // java.lang.ArithmeticException
    265         // java.lang.ArrayIndexOutOfBoundsException
    266         // java.lang.IllegalArgumentException
    267         // java.lang.IllegalAccessException
    268         // SecturityException
    269         // NumberFormatException
    270         // OutOfMemoryError 
    271         // StackOverflowError 
    272         // RuntimeException 
    273         regexMap.put(".*NullPointerException.*", "嘿,无中生有~Boom!");
    274         regexMap.put(".*ClassNotFoundException.*", "你确定你能找得到它?");
    275         regexMap.put(".*ArithmeticException.*", "我猜你的数学是体育老师教的,对吧?");
    276         regexMap.put(".*ArrayIndexOutOfBoundsException.*", "恩,无下限=无节操,请不要跟我搭话");
    277         regexMap.put(".*IllegalArgumentException.*", "你的出生就是一场错误。");
    278         regexMap.put(".*IllegalAccessException.*", "很遗憾,你的信用卡账号被冻结了,无权支付");
    279         regexMap.put(".*SecturityException.*", "死神马上降临");
    280         regexMap.put(".*NumberFormatException.*", "想要改变一下自己形象?去泰国吧,包你满意");
    281         regexMap.put(".*OutOfMemoryError.*", "或许你该减减肥了");
    282         regexMap.put(".*StackOverflowError.*", "啊,啊,憋不住了!");
    283         regexMap.put(".*RuntimeException.*", "你的人生走错了方向,重来吧");
    284 
    285     }
    286 
    287 }
    复制代码

    二、建立一个Application来全局监控

    复制代码
    import android.app.Application;
    
    public class CrashApplication extends Application {
    
        @Override
        public void onCreate() {
            super.onCreate();
            CrashHandler crashHandler = CrashHandler.getInstance();
            crashHandler.init(getApplicationContext());
        }
    
    }
    复制代码

    最后在配置文件中加入注册信息

    <application android:name=".CrashApplication" ... />

    和权限

        <!--uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /-->
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    提交错误日志到网络服务器这一块还没有添加。如果添加了这一块功能,就能够实时的得要用户使用时的错误日志,能够及时反馈不同机型不同时候发生的错误,能对我们开发者的后期维护带来极大的方便。

  • 相关阅读:
    HDU 3577Fast Arrangement(线段树模板之区间增减更新 区间求和查询)
    POJ 3468 A Simple Problem with Integers(线段树模板之区间增减更新 区间求和查询)
    HDU 1754 I Hate It(线段树之单点更新 区间最值查询)
    CSU 2056 a simple game (正反进行KMP)超级好题!!!
    HihoCoder
    HDU 6318 Swaps and Inversions 思路很巧妙!!!(转换为树状数组或者归并求解逆序数)
    51 Nod 1107 斜率小于0的连线数量 (转换为归并求逆序数或者直接树状数组,超级详细题解!!!)
    POJ 2299 Ultra-QuickSort 求逆序数 (归并或者数状数组)此题为树状数组入门题!!!
    C# DBHelper类 参考
    C#学习之接口
  • 原文地址:https://www.cnblogs.com/dongweiq/p/3951306.html
Copyright © 2020-2023  润新知