android 之 Crash信息的持久化处理
需求: 持久化运行时异常的信息
1.CrashHandler.java
import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Build; import android.os.Looper; import android.os.SystemClock; import android.widget.Toast; import java.io.File; import java.io.FileOutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.text.SimpleDateFormat; import java.util.HashMap; import java.util.Map; /** * 保存运行时异常的信息到手机上 * TODO 异常会保存3次, 应用会重启两次,这个不知道什么原因 */ public class CrashHandler implements Thread.UncaughtExceptionHandler { private final String FILE_NAME_FORMAT = "yyyy_MM_dd"; private final String FILE_NAME_PREFIXES = "crash_"; private final String FILE_NAME_EXTENSION = ".log"; private final String CRASH_TIME_FORMAT = "[yyyy-MM-dd HH:mm:ss:sss]"; private final String PARENT_DIRECTORY_NAME = "logs"; private Context mContext; private Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler; /** 文件路径 */ private String filePath; private String fileName; /////////////////////////////////////////////////////////////////////////// // 单例模式 private static CrashHandler mInstance = new CrashHandler(); private CrashHandler() {} public static CrashHandler getInstance() { return mInstance; } /////////////////////////////////////////////////////////////////////////// /** 在 自定义的Application 中 调用此方法即可 */ public void init(Context context) { mContext = context; filePath = mContext.getExternalFilesDir(PARENT_DIRECTORY_NAME) + File.separator; fileName = FILE_NAME_PREFIXES + new SimpleDateFormat(FILE_NAME_FORMAT).format(System.currentTimeMillis()) + FILE_NAME_EXTENSION; defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this); } /** 异常发生时,系统回调的函数,我们在这里处理一些操作 */ @Override public void uncaughtException(Thread thread, Throwable ex) { // 如果没有自定义处理方式就使用系统的方式 if (!handleException(ex) && defaultUncaughtExceptionHandler != null) { defaultUncaughtExceptionHandler.uncaughtException(thread, ex); } else { // 让线程停止一会是为了显示Toast信息给用户,然后Kill程序 SystemClock.sleep(1000); android.os.Process.killProcess(android.os.Process.myPid()); System.exit(0); } } /** 自定义 crash 处理 */ private boolean handleException(Throwable ex) { if (ex == null) return true; // 保存信息 saveInfo(mContext, ex); // 显示提示信息,需要在线程中显示Toast new Thread(new Runnable() { @Override public void run() { Looper.prepare(); Toast.makeText(mContext, "很抱歉,程序遭遇异常,即将退出!", Toast.LENGTH_SHORT).show(); Looper.loop(); } }).start(); return true; } /** 保存数据 */ private String saveInfo(Context context, Throwable ex) { StringBuffer sb = new StringBuffer("\n"); // crash 的时间 sb.append("crashTime = ").append(new SimpleDateFormat(CRASH_TIME_FORMAT).format(System.currentTimeMillis())).append("\n"); // 设备信息 for (Map.Entry<String, String> entry : obtainSimpleInfo(context).entrySet()) { String key = entry.getKey(); String value = entry.getValue(); sb.append(key).append(" = ").append(value).append("\n"); } // 异常信息 sb.append(obtainExceptionInfo(ex)); // 数据存储 File dir = new File(filePath); if (!dir.exists()) dir.mkdir(); try { FileOutputStream fos = new FileOutputStream(filePath + fileName, true); // 追加的方式 fos.write(sb.toString().getBytes()); fos.flush(); fos.close(); } catch (Exception e) { e.printStackTrace(); } return fileName; } /** 获取设备信息 */ private HashMap<String, String> obtainSimpleInfo(Context context) { HashMap<String, String> map = new HashMap<String, String>(); PackageManager mPackageManager = context.getPackageManager(); PackageInfo mPackageInfo = null; try { mPackageInfo = mPackageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } map.put("versionName", mPackageInfo.versionName); map.put("versionCode", "" + mPackageInfo.versionCode); map.put("MODEL", "" + Build.MODEL); map.put("SDK_INT", "" + Build.VERSION.SDK_INT); map.put("PRODUCT", "" + Build.PRODUCT); return map; } /** 获取 crash 信息 */ private String obtainExceptionInfo(Throwable throwable) { StringWriter mStringWriter = new StringWriter(); PrintWriter mPrintWriter = new PrintWriter(mStringWriter); throwable.printStackTrace(mPrintWriter); mPrintWriter.close(); return mStringWriter.toString(); } }
2.自定义的Application 中注册
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); CrashHandler.getInstance().init(this); throw new NullPointerException("test crash " + android.os.Process.myPid()); } }
3.adb shell cat /sdcard/Android/data/<package_name>/files/logs/crash_2016_04_20.log