若是某個應用安裝的Thread.UncaughtExceptionHandler未移交給默認的Thread.UncaughtExceptionHandler,則當出現未捕獲的異常時,系統不會終止應用,即不會出現系統默認的「抱歉,xxx應用已中止運行」.所以,咱們就能夠對應用的異常進行全局捕獲,從而將異常保存下來,方便之後分析問題.java
所以咱們能夠自定一個類去實現該接口,該類主要用於收集錯誤信息和保存錯誤信息.android
package com.example.yicangbuhuo; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Build; import android.os.Environment; import android.os.Looper; import android.text.TextUtils; import android.widget.Toast; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Field; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Map; /** * Created by asus on 2017/11/21. */ public class CrashHandler implements Thread.UncaughtExceptionHandler { private static CrashHandler sInstance = null; private Thread.UncaughtExceptionHandler mDefaultHandler; private Context mContext; // 保存手機信息和異常信息 private Map<String, String> mMessage = new HashMap<>(); public static CrashHandler getInstance() { if (sInstance == null) { synchronized (CrashHandler.class) { if (sInstance == null) { synchronized (CrashHandler.class) { sInstance = new CrashHandler(); } } } } return sInstance; } private CrashHandler() { } /** * 初始化默認異常捕獲 * * @param context context */ public void init(Context context) { mContext = context; // 獲取默認異常處理器 mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); // 將此類設爲默認異常處理器 Thread.setDefaultUncaughtExceptionHandler(this); } @Override public void uncaughtException(Thread t, Throwable e) { if (!handleException(e)) { // 未通過人爲處理,則調用系統默認處理異常,彈出系統強制關閉的對話框 if (mDefaultHandler != null) { mDefaultHandler.uncaughtException(t, e); } } else { // 已經人爲處理,系統本身退出 try { Thread.sleep(1000); } catch (InterruptedException e1) { e1.printStackTrace(); } android.os.Process.killProcess(android.os.Process.myPid()); System.exit(1); } } /** * 是否人爲捕獲異常 * * @param e Throwable * @return true:已處理 false:未處理 */ private boolean handleException(Throwable e) { if (e == null) {// 異常是否爲空 return false; } new Thread() {// 在主線程中彈出提示 @Override public void run() { Looper.prepare(); Toast.makeText(mContext, "捕獲到異常", Toast.LENGTH_SHORT).show(); Looper.loop(); } }.start(); collectErrorMessages(); saveErrorMessages(e); return false; } /** * 1.收集錯誤信息 */ private void collectErrorMessages() { PackageManager pm = mContext.getPackageManager(); try { PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES); if (pi != null) { String versionName = TextUtils.isEmpty(pi.versionName) ? "null" : pi.versionName; String versionCode = "" + pi.versionCode; mMessage.put("versionName", versionName); mMessage.put("versionCode", versionCode); } // 經過反射拿到錯誤信息 Field[] fields = Build.class.getFields(); if (fields != null && fields.length > 0) { for (Field field : fields) { field.setAccessible(true); try { mMessage.put(field.getName(), field.get(null).toString()); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } /** * 2.保存錯誤信息 * * @param e Throwable */ private void saveErrorMessages(Throwable e) { StringBuilder sb = new StringBuilder(); for (Map.Entry<String, String> entry : mMessage.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); sb.append(key).append("=").append(value).append("\n"); } Writer writer = new StringWriter(); PrintWriter pw = new PrintWriter(writer); e.printStackTrace(pw); Throwable cause = e.getCause(); // 循環取出Cause while (cause != null) { cause.printStackTrace(pw); cause = e.getCause(); } pw.close(); String result = writer.toString(); sb.append(result); String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA).format(new Date()); String fileName = "crash-" + time + "-" + System.currentTimeMillis() + ".log"; // 有無SD卡 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { String path = Environment.getExternalStorageDirectory().getPath() + "crash/"; File dir = new File(path); if (!dir.exists()) dir.mkdirs(); FileOutputStream fos = null; try { fos = new FileOutputStream(path + fileName); fos.write(sb.toString().getBytes()); } catch (Exception e1) { e1.printStackTrace(); } finally { if (fos != null) { try { fos.close(); } catch (IOException e1) { e1.printStackTrace(); } } } } } }
public class App extends Application { @Override public void onCreate() { super.onCreate(); CrashHandler.getInstance().init(this); } }