首页 > 系统 > Android > 正文

在Android中自定义捕获Application全局异常,可以替换掉系统的强制退出对话框

2019-11-09 18:28:51
字体:
来源:转载
供稿:网友

       大家都知道,现在安装 Android 系统的手机和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃

的现象,开发者个人不可能购买所有设备逐个调试,所以在程序发布出去之后,如果出现了崩溃现象,开发者应该及时获取在该设

备上导致崩溃的信息,这对于下一个版本的 BUG 修复帮助极大,所以今天就来介绍一下如何在程序崩溃的情况下收集相关的设备

参数信息和具体的异常信息,并发送这些信息到服务器供开发者分析和调试程序。

       遇到软件没有捕获的异常之后,系统会弹出这个默认的强制关闭对话框。

       我们当然不希望用户看到这种现象,简直是对用户心灵上的打击,而且对我们的 BUG 的修复也是毫无帮助的。我们需要的是软

件有一个全局的异常捕获器,当出现一个我们没有发现的异常时,捕获这个异常,并且将异常信息记录下来,上传到服务器公开发

这分出现异常的具体原因。

       接下来我们就来实现这一机制,不过首先我们还是来了解以下两个类:android.app.application

java.lang.Thread.UncaughtExceptionHandler。

    1、Application:用来管理应用程序的全局状态。在应用程序启动时 Application 会首先创建,然后才会根据情况(Intent)来启动

相应的Activity 和 Service。本示例中将在自定义加强版的 Application 中注册未捕获异常处理器。

    2、Thread.UncaughtExceptionHandler:线程未捕获异常处理器,用来处理未捕获异常。如果程序出现了未捕获异常,默认会弹

出系统中强制关闭对话框。我们需要实现此接口,并注册为程序中默认未捕获异常处理。这样当未捕获异常发生时,就可以做一些

个性化的异常处理操作。

 (1)新建java文件,CrashHandler.java 实现了 Thread.UncaughtExceptionHandler,使我们用来处理未捕获异常的主要成员,代

码如下:

public class CrashHandler implements UncaughtExceptionHandler {        public static final String TAG = "CrashHandler";        // CrashHandler 实例      PRivate static CrashHandler INSTANCE = new CrashHandler();        // 程序的 Context 对象      private Context mContext;        // 系统默认的 UncaughtException 处理类      private Thread.UncaughtExceptionHandler mDefaultHandler;        // 用来存储设备信息和异常信息      private Map<String, String> infos = new HashMap<String, String>();        // 用于格式化日期,作为日志文件名的一部分      private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");          /** 保证只有一个 CrashHandler 实例 */      private CrashHandler() {      }        /** 获取 CrashHandler 实例 ,单例模式 */      public static CrashHandler getInstance() {          return INSTANCE;      }        /**      * 初始化      *      * @param context      */      public void init(Context context) {          mContext = context;                // 获取系统默认的 UncaughtException 处理器          mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();                // 设置该 CrashHandler 为程序的默认处理器          Thread.setDefaultUncaughtExceptionHandler(this);      }        /**      * 当 UncaughtException 发生时会转入该函数来处理      */      @Override      public void uncaughtException(Thread thread, Throwable ex) {          if (!handleException(ex) && mDefaultHandler != null) {              // 如果用户没有处理则让系统默认的异常处理器来处理              mDefaultHandler.uncaughtException(thread, ex);          } else {              try {                  Thread.sleep(3000);              } catch (InterruptedException e) {                  Log.e(TAG, "error : ", e);              }                // 退出程序,注释下面的重启启动程序代码            android.os.Process.killProcess(android.os.Process.myPid());              System.exit(1);                  // 重新启动程序,注释上面的退出程序           Intent intent = new Intent();           intent.setClass(mContext,MainActivity.class);           intent.addFlag(Intent.FLAG_ACTIVITY_NEW_TASK);           mContext.startActivity(intent);           android.os.Process.killProcess(android.os.Process.myPid());        }      }        /**      * 自定义错误处理,收集错误信息,发送错误报告等操作均在此完成      *       * @param ex      * @return true:如果处理了该异常信息;否则返回 false      */      private boolean handleException(Throwable ex) {          if (ex == null) {              return false;          }            // 使用 Toast 来显示异常信息          new Thread() {              @Override              public void run() {                  Looper.prepare();                  Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出。", Toast.LENGTH_LONG).show();                  Looper.loop();              }          }.start();            // 收集设备参数信息          collectDeviceInfo(mContext);          // 保存日志文件          saveCrashInfo2File(ex);          return true;      }        /**      * 收集设备参数信息      * @param ctx      */      public void collectDeviceInfo(Context ctx) {          try {              PackageManager pm = ctx.getPackageManager();              PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);                if (pi != null) {                  String versionName = pi.versionName == null ? "null" : pi.versionName;                  String versionCode = pi.versionCode + "";                  infos.put("versionName", versionName);                  infos.put("versionCode", versionCode);              }          } catch (NameNotFoundException e) {              Log.e(TAG, "an error occured when collect package info", e);          }            Field[] fields = Build.class.getDeclaredFields();          for (Field field : fields) {              try {                  field.setaccessible(true);                  infos.put(field.getName(), field.get(null).toString());                  Log.d(TAG, field.getName() + " : " + field.get(null));              } catch (Exception e) {                  Log.e(TAG, "an error occured when collect crash info", e);              }          }      }        /**      * 保存错误信息到文件中     *      * @param ex      * @return  返回文件名称,便于将文件传送到服务器      */      private String saveCrashInfo2File(Throwable ex) {          StringBuffer sb = new StringBuffer();          for (Map.Entry<String, String> entry : infos.entrySet()) {              String key = entry.getKey();              String value = entry.getValue();              sb.append(key + "=" + value + "/n");          }            Writer writer = new StringWriter();          PrintWriter printWriter = new PrintWriter(writer);          ex.printStackTrace(printWriter);          Throwable cause = ex.getCause();          while (cause != null) {              cause.printStackTrace(printWriter);              cause = cause.getCause();          }          printWriter.close();            String result = writer.toString();          sb.append(result);          try {              long timestamp = System.currentTimeMillis();              String time = formatter.format(new Date());              String fileName = "crash-" + time + "-" + timestamp + ".log";                            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {                  String path = "/sdcard/crash/";                  File dir = new File(path);                  if (!dir.exists()) {                      dir.mkdirs();                  }                  FileOutputStream fos = new FileOutputStream(path + fileName);                  fos.write(sb.toString().getBytes());                  fos.close();              }                return fileName;          } catch (Exception e) {              Log.e(TAG, "an error occured while writing file...", e);          }            return null;      }  }  (2)完成这个 CrashHandler 后,我们需要在一个 Application 环境中让其运行,为此,我们继承 android.app.Application,添加自己的代码,CrashApplication.java代码如下:package com.scott.crash;    import android.app.Application;    public class CrashApplication extends Application {            @Override      public void onCreate() {          super.onCreate();          CrashHandler crashHandler = CrashHandler.getInstance();          crashHandler.init(getApplicationContext());      }    }  

   

(3)因为我们上面的 CrashHandler 中,遇到异常后要保存设备参数和具体异常信息到 SDCARD,所以我们需要在

 AndroidManifest.xml 中加入读写 SDCARD 权限:

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


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表