| 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.lib_coremodel.util.SpUtil; | 
|   | 
| import java.io.ByteArrayOutputStream; | 
| 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卡中 | 
|             dumpExceptionToSDCard(ex); | 
|             //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); | 
|             //这里可以通过网络上传异常信息到服务器,便于开发人员分析日志从而解决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);   } | 
| } |