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 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卡中 dumpExceptionToSDCard(ex); //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); } }