MrShi
5 天以前 3a154bdb0a5aaa2c0ac3eac95a6ba747068bd454
keyCabinet-android/app/src/main/java/com/doumee/keyCabinet/utils/CrashHandler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,343 @@
package com.doumee.keyCabinet.utils;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import com.doumee.keyCabinet.ui.guide.GuideActivity;
import com.doumee.keyCabinet.ui.main.MainActivity;
import com.doumee.lib_coremodel.util.SpUtil;
import com.innohi.YNHAPI;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
/**
 *
 *
 * UncaughtExceptionHandler:线程未捕获异常控制器是用来处理未捕获异常的。 å¦‚果程序出现了未捕获异常默认情况下则会出现强行关闭对话框
 * å®žçŽ°è¯¥æŽ¥å£å¹¶æ³¨å†Œä¸ºç¨‹åºä¸­çš„é»˜è®¤æœªæ•èŽ·å¼‚å¸¸å¤„ç† è¿™æ ·å½“未捕获异常发生时,就可以做些异常处理操作 ä¾‹å¦‚:收集异常信息,发送错误报告 ç­‰ã€‚
 *
 * UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告.
 */
public class CrashHandler implements UncaughtExceptionHandler {
   private static final String TAG = "CrashHandler";
   private static String PATH = Environment.getExternalStorageDirectory().getPath() + "/log/";
   public static final String FILE_NAME = "crash";
   //log文件的后缀名
   private static final String FILE_NAME_SUFFIX = ".txt";
   public static final String CLIENT_VERSION = "clientVersion";
   /** CrashHandler实例 */
   private static CrashHandler INSTANCE;
   /** ç¨‹åºçš„Context对象 */
   private Context mContext;
   /** ç³»ç»Ÿé»˜è®¤çš„UncaughtException处理类 */
   private UncaughtExceptionHandler mDefaultHandler;
   /** ä½¿ç”¨Properties来保存设备的信息和错误堆栈信息 */
   private Properties mDeviceCrashInfo = new Properties();
   /** ä¿è¯åªæœ‰ä¸€ä¸ª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;
      //PATH = ctx.getFilesDir().getParentFile().getAbsolutePath()+ "/log/";
      mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
      Thread.setDefaultUncaughtExceptionHandler(this);
      // æ”¶é›†è®¾å¤‡ä¿¡æ¯
      collectCrashDeviceInfo(mContext);
   }
   @Override
   public void uncaughtException(Thread thread, Throwable ex) {
      try {
         //导出异常信息到SD卡中
         YNHAPI mAPI = YNHAPI.getInstance();
         mAPI.setNavigationBarVisibility(YNHAPI.NavigationBarVisibility.VISIBLE);
         dumpExceptionToSDCard(ex);
         long time = SpUtil.getLong("err_app");
         SpUtil.saveLong("err_app",System.currentTimeMillis());
         if(System.currentTimeMillis()-time>60000){
            //报错间隔60秒,才重启
            //System.out.println("重启应用");
            // åˆ›å»ºä¸€ä¸ªæ–°çš„启动意图
            Intent intent = new Intent(mContext, MainActivity.class);
            // è®¾ç½®FLAG_ACTIVITY_CLEAR_TASK标志位
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
            // ä½¿ç”¨PendingIntent包装启动意图
            PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
            // èŽ·å–AlarmManager实例
            AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
            // åœ¨500毫秒后触发重启操作
            alarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + 10, pendingIntent);
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(1);
         }
         //这里可以通过网络上传异常信息到服务器,便于开发人员分析日志从而解决bug
      } catch (IOException e) {
         e.printStackTrace();
         System.out.println("错误日志1"+e.getMessage());
      }
      //打印出当前调用栈信息
      ex.printStackTrace();
      //如果系统提供了默认的异常处理器,则交给系统去结束我们的程序,否则就由我们自己结束自己
      if (mDefaultHandler != null) {
         mDefaultHandler.uncaughtException(thread, ex);
      } else {
         android.os.Process.killProcess(android.os.Process.myPid());
      }
      if (!handleException(ex) && mDefaultHandler != null) {
         // å¦‚果用户没有处理则让系统默认的异常处理器来处理
         mDefaultHandler.uncaughtException(thread, ex);
      } else {
         /*try {
            Thread.sleep(500);
         } catch (InterruptedException e) {
            Log.e(TAG, "error : ", e);
         }*/
         /*System.out.println("重启应用");
         // åˆ›å»ºä¸€ä¸ªæ–°çš„启动意图
         Intent intent = new Intent(mContext, GuideActivity.class);
         // è®¾ç½®FLAG_ACTIVITY_CLEAR_TASK标志位
         intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
         // ä½¿ç”¨PendingIntent包装启动意图
         PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
         // èŽ·å–AlarmManager实例
         AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         // åœ¨500毫秒后触发重启操作
         alarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + 10, pendingIntent);
         android.os.Process.killProcess(android.os.Process.myPid());
         System.exit(1);*/
         /*// æœªæ•获异常写文件
         writeCrashInfoToFile(ex);
         // é€€å‡ºæ•´ä¸ªåº”用
         if (mDefaultHandler != null) {
            mDefaultHandler.uncaughtException(thread, ex);
         } else {
            System.exit(0);
         }*/
      }
   }
   /**
    * è‡ªå®šä¹‰é”™è¯¯å¤„理,收集错误信息 å‘送错误报告等操作均在此完成. å¼€å‘者可以根据自己的情况来自定义异常处理逻辑
    *
    * @param ex
    * @return true:如果处理了该异常信息;否则返回false
    */
   private boolean handleException(Throwable ex) {
      if (ex == null) {
         return false;
      }
      // ä½¿ç”¨Toast来显示异常信息
      new Thread() {
         @Override
         public void run() {
            Looper.prepare();
            //T.ss("很抱歉,程序出现异常,即将退出");
            Looper.loop();
         }
      }.start();
      // æœªæ•获异常写文件
      writeCrashInfoToFile(ex);
      return true;
   }
   /**
    * æ”¶é›†ç¨‹åºå´©æºƒçš„设备信息
    *
    * @param ctx
    */
   private void collectCrashDeviceInfo(Context ctx) {
      try {
         PackageManager pm = ctx.getPackageManager();
         PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),
               PackageManager.GET_ACTIVITIES);
         if (pi != null) {
            mDeviceCrashInfo.setProperty(CLIENT_VERSION,
                  pi.versionName == null ? "not set" : pi.versionName);
         }
      } catch (NameNotFoundException e) {
         e.printStackTrace();
         System.out.println("错误日志3"+e.getMessage());
      }
   }
   /**
    * å°†å¼‚常信息写入日志文件
    *
    * @param ex
    * @return
    */
   private String writeCrashInfoToFile(Throwable ex) {
      Writer info = new StringWriter();
      PrintWriter printWriter = new PrintWriter(info);
      // printStackTrace(PrintWriter s)
      // å°†æ­¤ throwable åŠå…¶è¿½è¸ªè¾“出到指定的 PrintWriter
      ex.printStackTrace(printWriter);
      // getCause() è¿”回此 throwable çš„ cause;如果 cause ä¸å­˜åœ¨æˆ–未知,则返回 null。
      Throwable cause = ex.getCause();
      while (cause != null) {
         cause.printStackTrace(printWriter);
         cause = cause.getCause();
      }
      // toString() ä»¥å­—符串的形式返回该缓冲区的当前值。
      String result = info.toString();
      printWriter.close();
      String errorLog = String
            .format("FINGERPRINT=%s|"
                  + "STACK_TRACE=%s|versionName=%s|MODEL=%s|MANUFACTURER=%s|BRAND=%s|RELEASE=%s",
            // ç¡¬ä»¶åç§°
                  Build.FINGERPRINT,
                  // å¼‚常信息
                  result,
                  // åº”用版本名称
                  mDeviceCrashInfo.getProperty(CLIENT_VERSION),
                  // è®¾å¤‡åç§°
                  Build.MODEL,
                  // ç¡¬ä»¶åˆ¶é€ å•†
                  Build.MANUFACTURER,
                  // android系统定制商
                  Build.BRAND,
                  // android SDK版本
                  Build.VERSION.RELEASE);
      //sp.setString(Constants.SP_CRASH_EXCEPTION, errorLog);
      return null;
   }
   private void dumpExceptionToSDCard(Throwable ex) throws IOException {
      //如果SD卡不存在或无法使用,则无法把异常信息写入SD卡
      if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
         /*if (Const.DEBUG) {
            LogUtils.w(TAG, "sdcard unmounted,skip dump exception");
            return;
         }*/
         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));
      try {
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         PrintStream print = new PrintStream(out);
         //导出发生异常的时间
         print.println(time);
         //导出手机信息
         dumpPhoneInfo(print);
         print.println();
         ex.printStackTrace(print);
         //SpUtil.saveString(FILE_NAME+time,new String(out.toByteArray()));
         print.close();
      } catch (Exception e) {
         e.printStackTrace();
         System.out.println("错误日志2"+e.getMessage());
         //LogUtils.e(TAG, "dump crash info failed");
      }
      //以当前时间创建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();
         //导出异常的调用栈信息
         ex.printStackTrace(pw);
         pw.close();
      } catch (Exception e) {
         e.printStackTrace();
         System.out.println("错误日志2"+e.getMessage());
         //LogUtils.e(TAG, "dump crash info failed");
      }
   }
   private void dumpPhoneInfo(PrintStream pw) throws 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 dumpPhoneInfo(PrintWriter pw) throws 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);   }
}