Android自定义捕获Application全局异常优化版

    xiaoxiao2021-12-14  17

    参考:

    http://bigcat.easymorse.com/?p=1152

    [java] view plain copy print ? package qianlong.qlmobile.ui;    import java.io.File;  import java.io.FileOutputStream;  import java.io.FilenameFilter;  import java.io.PrintWriter;  import java.io.StringWriter;  import java.io.Writer;  import java.lang.Thread.UncaughtExceptionHandler;  import java.lang.reflect.Field;  import java.util.Arrays;  import java.util.Properties;  import java.util.TreeSet;    import android.content.Context;  import android.content.pm.PackageInfo;  import android.content.pm.PackageManager;  import android.content.pm.PackageManager.NameNotFoundException;  import android.os.Build;  import android.os.Looper;  import android.text.format.Time;  import android.util.Log;  import android.view.Gravity;  import android.widget.Toast;    public class CrashHandler implements UncaughtExceptionHandler {        /** Debug Log tag*/       public static final String TAG = "CrashHandler";       /** 是否开启日志输出,在Debug状态下开启,      * 在Release状态下关闭以提示程序性能      * */       public static final boolean DEBUG = false;       /** 系统默认的UncaughtException处理类 */       private Thread.UncaughtExceptionHandler mDefaultHandler;       /** CrashHandler实例 */       private static CrashHandler INSTANCE;       /** 程序的Context对象 */       private Context mContext;       /** 使用Properties来保存设备的信息和错误堆栈信息*/       private Properties mDeviceCrashInfo = new Properties();       private static final String VERSION_NAME = "versionName";       private static final String VERSION_CODE = "versionCode";       private static final String STACK_TRACE = "STACK_TRACE";       /** 错误报告文件的扩展名 */       private static final String CRASH_REPORTER_EXTENSION = ".cr";             /** 保证只有一个CrashHandler实例 */       private CrashHandler() {}             /** 获取CrashHandler实例 ,单例模式*/       public static CrashHandler getInstance() {           if (INSTANCE == null) {               INSTANCE = new CrashHandler();           }           return INSTANCE;       }             /**      * 初始化,注册Context对象,      * 获取系统默认的UncaughtException处理器,      * 设置该CrashHandler为程序的默认处理器      * @param ctx      */       public void init(Context ctx) {           mContext = ctx;           mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();           Thread.setDefaultUncaughtExceptionHandler(this);       }             /**      * 当UncaughtException发生时会转入该函数来处理      */       @Override       public void uncaughtException(Thread thread, Throwable ex) {           if (!handleException(ex) && mDefaultHandler != null) {               //如果用户没有处理则让系统默认的异常处理器来处理               mDefaultHandler.uncaughtException(thread, ex);           } else {               //Sleep一会后结束程序               try {                   Thread.sleep(5000);               } catch (InterruptedException e) {                   Log.e(TAG, "Error : ", e);               }               android.os.Process.killProcess(android.os.Process.myPid());               System.exit(10);           }       }             /**      * 自定义错误处理,收集错误信息      * 发送错误报告等操作均在此完成.      * 开发者可以根据自己的情况来自定义异常处理逻辑      * @param ex      * @return true:如果处理了该异常信息;否则返回false      */       private boolean handleException(Throwable ex) {           if (ex == null) {               Log.w(TAG, "handleException --- ex==null");               return true;           }           final String msg = ex.getLocalizedMessage();           if(msg == null) {              return false;          }          //使用Toast来显示异常信息           new Thread() {               @Override               public void run() {                   Looper.prepare();                   Toast toast = Toast.makeText(mContext, "程序出错,即将退出:\r\n" + msg,                          Toast.LENGTH_LONG);                  toast.setGravity(Gravity.CENTER, 00);                  toast.show();  //              MsgPrompt.showMsg(mContext, "程序出错啦", msg+"\n点确认退出");                  Looper.loop();               }           }.start();           //收集设备信息           collectCrashDeviceInfo(mContext);           //保存错误报告文件           saveCrashInfoToFile(ex);           //发送错误报告到服务器           //sendCrashReportsToServer(mContext);           return true;       }             /**      * 在程序启动时候, 可以调用该函数来发送以前没有发送的报告      */       public void sendPreviousReportsToServer() {           sendCrashReportsToServer(mContext);       }       /**      * 把错误报告发送给服务器,包含新产生的和以前没发送的.      * @param ctx      */       private void sendCrashReportsToServer(Context ctx) {           String[] crFiles = getCrashReportFiles(ctx);           if (crFiles != null && crFiles.length > 0) {           TreeSet<String> sortedFiles = new TreeSet<String>();           sortedFiles.addAll(Arrays.asList(crFiles));           for (String fileName : sortedFiles) {           File cr = new File(ctx.getFilesDir(), fileName);           postReport(cr);           cr.delete();// 删除已发送的报告           }           }       }       private void postReport(File file) {           // TODO 发送错误报告到服务器       }             /**      * 获取错误报告文件名      * @param ctx      * @return      */       private String[] getCrashReportFiles(Context ctx) {           File filesDir = ctx.getFilesDir();           FilenameFilter filter = new FilenameFilter() {               public boolean accept(File dir, String name) {                   return name.endsWith(CRASH_REPORTER_EXTENSION);               }           };           return filesDir.list(filter);       }             /**      * 保存错误信息到文件中      * @param ex      * @return      */       private String saveCrashInfoToFile(Throwable ex) {           Writer info = new StringWriter();           PrintWriter printWriter = new PrintWriter(info);           ex.printStackTrace(printWriter);           Throwable cause = ex.getCause();           while (cause != null) {               cause.printStackTrace(printWriter);               cause = cause.getCause();           }           String result = info.toString();           printWriter.close();           mDeviceCrashInfo.put("EXEPTION", ex.getLocalizedMessage());          mDeviceCrashInfo.put(STACK_TRACE, result);           try {               //long timestamp = System.currentTimeMillis();               Time t = new Time("GMT+8");               t.setToNow(); // 取得系统时间              int date = t.year * 10000 + t.month * 100 + t.monthDay;              int time = t.hour * 10000 + t.minute * 100 + t.second;              String fileName = "crash-" + date + "-" + time + CRASH_REPORTER_EXTENSION;               FileOutputStream trace = mContext.openFileOutput(fileName,                       Context.MODE_PRIVATE);               mDeviceCrashInfo.store(trace, "");               trace.flush();               trace.close();               return fileName;           } catch (Exception e) {               Log.e(TAG, "an error occured while writing report file...", e);           }           return null;       }         /**      * 收集程序崩溃的设备信息      *      * @param ctx      */       public void collectCrashDeviceInfo(Context ctx) {           try {               PackageManager pm = ctx.getPackageManager();               PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),                       PackageManager.GET_ACTIVITIES);               if (pi != null) {                   mDeviceCrashInfo.put(VERSION_NAME,                           pi.versionName == null ? "not set" : pi.versionName);                   mDeviceCrashInfo.put(VERSION_CODE, ""+pi.versionCode);               }           } catch (NameNotFoundException e) {               Log.e(TAG, "Error while collect package info", e);           }           //使用反射来收集设备信息.在Build类中包含各种设备信息,           //例如: 系统版本号,设备生产商 等帮助调试程序的有用信息           //具体信息请参考后面的截图           Field[] fields = Build.class.getDeclaredFields();           for (Field field : fields) {               try {                   field.setAccessible(true);                   mDeviceCrashInfo.put(field.getName(), ""+field.get(null));                   if (DEBUG) {                       Log.d(TAG, field.getName() + " : " + field.get(null));                   }               } catch (Exception e) {                   Log.e(TAG, "Error while collect crash info", e);               }           }       }     }   package qianlong.qlmobile.ui; import java.io.File; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.lang.Thread.UncaughtExceptionHandler; import java.lang.reflect.Field; import java.util.Arrays; import java.util.Properties; import java.util.TreeSet; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Build; import android.os.Looper; import android.text.format.Time; import android.util.Log; import android.view.Gravity; import android.widget.Toast; public class CrashHandler implements UncaughtExceptionHandler { /** Debug Log tag*/ public static final String TAG = "CrashHandler"; /** 是否开启日志输出,在Debug状态下开启, * 在Release状态下关闭以提示程序性能 * */ public static final boolean DEBUG = false; /** 系统默认的UncaughtException处理类 */ private Thread.UncaughtExceptionHandler mDefaultHandler; /** CrashHandler实例 */ private static CrashHandler INSTANCE; /** 程序的Context对象 */ private Context mContext; /** 使用Properties来保存设备的信息和错误堆栈信息*/ private Properties mDeviceCrashInfo = new Properties(); private static final String VERSION_NAME = "versionName"; private static final String VERSION_CODE = "versionCode"; private static final String STACK_TRACE = "STACK_TRACE"; /** 错误报告文件的扩展名 */ private static final String CRASH_REPORTER_EXTENSION = ".cr"; /** 保证只有一个CrashHandler实例 */ private CrashHandler() {} /** 获取CrashHandler实例 ,单例模式*/ public static CrashHandler getInstance() { if (INSTANCE == null) { INSTANCE = new CrashHandler(); } return INSTANCE; } /** * 初始化,注册Context对象, * 获取系统默认的UncaughtException处理器, * 设置该CrashHandler为程序的默认处理器 * @param ctx */ public void init(Context ctx) { mContext = ctx; mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this); } /** * 当UncaughtException发生时会转入该函数来处理 */ @Override public void uncaughtException(Thread thread, Throwable ex) { if (!handleException(ex) && mDefaultHandler != null) { //如果用户没有处理则让系统默认的异常处理器来处理 mDefaultHandler.uncaughtException(thread, ex); } else { //Sleep一会后结束程序 try { Thread.sleep(5000); } catch (InterruptedException e) { Log.e(TAG, "Error : ", e); } android.os.Process.killProcess(android.os.Process.myPid()); System.exit(10); } } /** * 自定义错误处理,收集错误信息 * 发送错误报告等操作均在此完成. * 开发者可以根据自己的情况来自定义异常处理逻辑 * @param ex * @return true:如果处理了该异常信息;否则返回false */ private boolean handleException(Throwable ex) { if (ex == null) { Log.w(TAG, "handleException --- ex==null"); return true; } final String msg = ex.getLocalizedMessage(); if(msg == null) { return false; } //使用Toast来显示异常信息 new Thread() { @Override public void run() { Looper.prepare(); Toast toast = Toast.makeText(mContext, "程序出错,即将退出:\r\n" + msg, Toast.LENGTH_LONG); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); // MsgPrompt.showMsg(mContext, "程序出错啦", msg+"\n点确认退出"); Looper.loop(); } }.start(); //收集设备信息 collectCrashDeviceInfo(mContext); //保存错误报告文件 saveCrashInfoToFile(ex); //发送错误报告到服务器 //sendCrashReportsToServer(mContext); return true; } /** * 在程序启动时候, 可以调用该函数来发送以前没有发送的报告 */ public void sendPreviousReportsToServer() { sendCrashReportsToServer(mContext); } /** * 把错误报告发送给服务器,包含新产生的和以前没发送的. * @param ctx */ private void sendCrashReportsToServer(Context ctx) { String[] crFiles = getCrashReportFiles(ctx); if (crFiles != null && crFiles.length > 0) { TreeSet<String> sortedFiles = new TreeSet<String>(); sortedFiles.addAll(Arrays.asList(crFiles)); for (String fileName : sortedFiles) { File cr = new File(ctx.getFilesDir(), fileName); postReport(cr); cr.delete();// 删除已发送的报告 } } } private void postReport(File file) { // TODO 发送错误报告到服务器 } /** * 获取错误报告文件名 * @param ctx * @return */ private String[] getCrashReportFiles(Context ctx) { File filesDir = ctx.getFilesDir(); FilenameFilter filter = new FilenameFilter() { public boolean accept(File dir, String name) { return name.endsWith(CRASH_REPORTER_EXTENSION); } }; return filesDir.list(filter); } /** * 保存错误信息到文件中 * @param ex * @return */ private String saveCrashInfoToFile(Throwable ex) { Writer info = new StringWriter(); PrintWriter printWriter = new PrintWriter(info); ex.printStackTrace(printWriter); Throwable cause = ex.getCause(); while (cause != null) { cause.printStackTrace(printWriter); cause = cause.getCause(); } String result = info.toString(); printWriter.close(); mDeviceCrashInfo.put("EXEPTION", ex.getLocalizedMessage()); mDeviceCrashInfo.put(STACK_TRACE, result); try { //long timestamp = System.currentTimeMillis(); Time t = new Time("GMT+8"); t.setToNow(); // 取得系统时间 int date = t.year * 10000 + t.month * 100 + t.monthDay; int time = t.hour * 10000 + t.minute * 100 + t.second; String fileName = "crash-" + date + "-" + time + CRASH_REPORTER_EXTENSION; FileOutputStream trace = mContext.openFileOutput(fileName, Context.MODE_PRIVATE); mDeviceCrashInfo.store(trace, ""); trace.flush(); trace.close(); return fileName; } catch (Exception e) { Log.e(TAG, "an error occured while writing report file...", e); } return null; } /** * 收集程序崩溃的设备信息 * * @param ctx */ public void collectCrashDeviceInfo(Context ctx) { try { PackageManager pm = ctx.getPackageManager(); PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES); if (pi != null) { mDeviceCrashInfo.put(VERSION_NAME, pi.versionName == null ? "not set" : pi.versionName); mDeviceCrashInfo.put(VERSION_CODE, ""+pi.versionCode); } } catch (NameNotFoundException e) { Log.e(TAG, "Error while collect package info", e); } //使用反射来收集设备信息.在Build类中包含各种设备信息, //例如: 系统版本号,设备生产商 等帮助调试程序的有用信息 //具体信息请参考后面的截图 Field[] fields = Build.class.getDeclaredFields(); for (Field field : fields) { try { field.setAccessible(true); mDeviceCrashInfo.put(field.getName(), ""+field.get(null)); if (DEBUG) { Log.d(TAG, field.getName() + " : " + field.get(null)); } } catch (Exception e) { Log.e(TAG, "Error while collect crash info", e); } } } } [java] view plain copy print ? 用法:  //全局数据存储  public class App extends Application {  private final static float HEAP_UTILIZATION = 0.75f;  private final static int MIN_HEAP_SIZE = 610241024 ;     @Override     public void onCreate() {      super.onCreate();                // 异常处理,不需要处理时注释掉这两句即可!      CrashHandler crashHandler = CrashHandler.getInstance();       // 注册crashHandler       crashHandler.init(getApplicationContext());                 // 优化内存,以下非必须!      VMRuntime.getRuntime().setTargetHeapUtilization(HEAP_UTILIZATION);      VMRuntime.getRuntime().setMinimumHeapSize(MIN_HEAP_SIZE);           //changeMetrics(this);//修改屏幕Density          ......     }  }   用法: //全局数据存储 public class App extends Application { private final static float HEAP_UTILIZATION = 0.75f; private final static int MIN_HEAP_SIZE = 6* 1024* 1024 ; @Override public void onCreate() { super.onCreate(); // 异常处理,不需要处理时注释掉这两句即可! CrashHandler crashHandler = CrashHandler.getInstance(); // 注册crashHandler crashHandler.init(getApplicationContext()); // 优化内存,以下非必须! VMRuntime.getRuntime().setTargetHeapUtilization(HEAP_UTILIZATION); VMRuntime.getRuntime().setMinimumHeapSize(MIN_HEAP_SIZE); //changeMetrics(this);//修改屏幕Density ...... } } 异常时写入文件,下面是data/data中生成的文件: [java] view plain copy print ? //  private static final boolean DebugFlag = false;  //修改屏幕Density  public static void changeMetrics(Context context) {          DisplayMetrics curMetrics = context.getResources().getDisplayMetrics();          if(!DebugFlag) {              if (curMetrics.densityDpi == DisplayMetrics.DENSITY_HIGH) {                  DisplayMetrics metrics = new DisplayMetrics();                  metrics.scaledDensity = 1.0f;                  metrics.density = 1.0f;                  metrics.densityDpi = DisplayMetrics.DENSITY_MEDIUM;                  metrics.xdpi = DisplayMetrics.DENSITY_MEDIUM;                  metrics.ydpi = DisplayMetrics.DENSITY_MEDIUM;                  metrics.heightPixels = curMetrics.heightPixels;                  metrics.widthPixels = curMetrics.widthPixels;                  context.getResources().getDisplayMetrics().setTo(metrics);              }          } else {              DisplayMetrics metrics = new DisplayMetrics();              metrics.scaledDensity = (float)(130/160.0);              metrics.density = (float)(130/160.0);              metrics.densityDpi = 130;              metrics.xdpi = 130;              metrics.ydpi = 130;              metrics.heightPixels = curMetrics.heightPixels;              metrics.widthPixels = curMetrics.widthPixels;              context.getResources().getDisplayMetrics().setTo(metrics);          }  }   // private static final boolean DebugFlag = false; //修改屏幕Density public static void changeMetrics(Context context) { DisplayMetrics curMetrics = context.getResources().getDisplayMetrics(); if(!DebugFlag) { if (curMetrics.densityDpi == DisplayMetrics.DENSITY_HIGH) { DisplayMetrics metrics = new DisplayMetrics(); metrics.scaledDensity = 1.0f; metrics.density = 1.0f; metrics.densityDpi = DisplayMetrics.DENSITY_MEDIUM; metrics.xdpi = DisplayMetrics.DENSITY_MEDIUM; metrics.ydpi = DisplayMetrics.DENSITY_MEDIUM; metrics.heightPixels = curMetrics.heightPixels; metrics.widthPixels = curMetrics.widthPixels; context.getResources().getDisplayMetrics().setTo(metrics); } } else { DisplayMetrics metrics = new DisplayMetrics(); metrics.scaledDensity = (float)(130/160.0); metrics.density = (float)(130/160.0); metrics.densityDpi = 130; metrics.xdpi = 130; metrics.ydpi = 130; metrics.heightPixels = curMetrics.heightPixels; metrics.widthPixels = curMetrics.widthPixels; context.getResources().getDisplayMetrics().setTo(metrics); } }

    转载请注明原文地址: https://ju.6miu.com/read-964294.html

    最新回复(0)