Android 全局异常捕获之CrashHandler
一个App上线或者投入到生产环境的时候崩溃了,还不知道是什么原因,这肯定是开发者的痛…所以肯定要加入全局异常捕获,如果项目较大的话,可以考虑加入第三方诸如友盟的崩溃统计插件,以达到异常捕获的效果!
Crash,可以理解为崩溃、垮台,通常来讲就是App运行期间发生了不可预料的错误,虽然在经历发布之前,测试人员进行了大量的测试,但是并不能保证App的正常运行,总会或多或少有一些BUG的。
Java的Thread中有一个UncaughtExceptionHandler接口,该接口的作用主要是为了当Thread 因未捕获的异常而突然终止时,调用处理程序。我们可以通过setDefaultUncaughtExceptionHandler方法,来改变异常默认处理程序。
实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 import android.content.Context;import android.content.pm.PackageInfo;import android.content.pm.PackageManager;import android.content.pm.PackageManager.NameNotFoundException;import android.os.Build;import java.io.PrintWriter;import java.io.StringWriter;import java.io.Writer;import java.lang.Thread.UncaughtExceptionHandler;import java.lang.reflect.Field;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.HashMap;import java.util.Map;public class CrashHandler implements UncaughtExceptionHandler { private Thread.UncaughtExceptionHandler mDefaultHandler; private static CrashHandler INSTANCE; private Context mContext; private Map<String, String> infos = new HashMap <String, String>(); private CrashHandler () { } public static CrashHandler getInstance () { if (INSTANCE == null ) INSTANCE = new CrashHandler (); return INSTANCE; } public void init (Context context) { mContext = context; mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this ); } @Override public void uncaughtException (Thread thread, Throwable ex) { if (!handleException(ex) && mDefaultHandler != null ) { mDefaultHandler.uncaughtException(thread, ex); } else { try { Thread.sleep(3000 ); } catch (InterruptedException e) { LogUtils.e(e.toString()); } android.os.Process.killProcess(android.os.Process.myPid()); System.exit(1 ); } } private boolean handleException (Throwable ex) { if (ex == null ) { return false ; } collectDeviceInfo(mContext); saveCrashInfo2File(ex); return true ; } public void collectDeviceInfo (Context ctx) { try { PackageManager pm = ctx.getPackageManager(); PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES); if (pi != null ) { String versionName = pi.versionName == null ? "null" : pi.versionName; String versionCode = pi.versionCode + "" ; infos.put("versionName" , versionName); infos.put("versionCode" , versionCode); } } catch (NameNotFoundException e) { LogUtils.e("CrashHandleran.NameNotFoundException---> error occured when collect package info" , e); } Field[] fields = Build.class.getDeclaredFields(); for (Field field : fields) { try { field.setAccessible(true ); infos.put(field.getName(), field.get(null ).toString()); } catch (Exception e) { LogUtils.e("CrashHandler.NameNotFoundException---> an error occured when collect crash info" , e); } } } private String saveCrashInfo2File (Throwable ex) { StringBuffer sb = new StringBuffer (); sb.append("---------------------sta--------------------------" ); for (Map.Entry<String, String> entry : infos.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); sb.append(key + "=" + value + "\n" ); } Writer writer = new StringWriter (); PrintWriter printWriter = new PrintWriter (writer); ex.printStackTrace(printWriter); Throwable cause = ex.getCause(); while (cause != null ) { cause.printStackTrace(printWriter); cause = cause.getCause(); } printWriter.close(); String result = writer.toString(); sb.append(result); sb.append("--------------------end---------------------------" ); LogUtils.e(sb.toString()); return null ; } } 然后我们在Application中,对CrashHandler进行初始化。 import android.app.Application;import android.content.Context;import com.yuyh.utils.CrashHandler;public class App extends Application { public static Context mContext; @Override public void onCreate () { super .onCreate(); CrashHandler crashHandler = CrashHandler.getInstance(); crashHandler.init(getApplicationContext()); mContext = this ; } }
这样的话,当程序代码中并未捕获异常,但发生了异常的时候,就会交由CrashHandler
进行处理,异常信息可以保存到日志文件中。日志文件记录请参考:Android 日志打印工具类 可显示打印所在的方法和行号 , 这样子,就能把异常信息及其异常发生所在的位置,保存在日志文件中。