开发过程中捕获异常并定位问题解决问题是基本之一,当然也有很多第三方的平台,比如接入友盟统计、第三方加固(比如360加固等)、腾讯Bugly等都会为我们收集到异常日志。但是,我个人认为开发及测试过程中编写一个Crash收集工具类尤为重要。下面分享一下我的crash处理。直接上代码吧,定义一个CrashHandler工具类
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.util.Log; import android.widget.Toast; import java.io.File; import java.io.FileOutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.lang.reflect.Field; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; import static android.content.ContentValues.TAG; /** * 统一错误捕获类 * Created by p on 2018/5/16. */ public class CrashHandler implements Thread.UncaughtExceptionHandler { private Context mContext; // CrashHandler实例 private volatile static CrashHandler instance; //系统默认的UncaughtException处理类 private Thread.UncaughtExceptionHandler mDefaultHandler; //用来存储设备信息和异常信息 private Map<String, String> infoMap = new HashMap<String, String>(); private CrashHandler(Context context) { mContext = context.getApplicationContext(); } /** * 单利模式 保证对象唯一 * @param context * @return */ public static CrashHandler getInstance(Context context) { if (instance == null) { synchronized (CrashHandler.class) { if (instance == null) { if (context == null) { throw new NullPointerException("context cannet NULL"); } instance = new CrashHandler(context); } } } return instance; } public void init() { //获取系统默认的UncaughtException处理器 mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); //设置该CrashHandler为程序的默认处理器 Thread.setDefaultUncaughtExceptionHandler(this); } @Override public void uncaughtException(Thread t, Throwable e) { if (!handleException(e) && mDefaultHandler != null) { //如果用户没有处理则让系统默认的异常处理器来处理 mDefaultHandler.uncaughtException(t, e); } else { try { Thread.sleep(3000); } catch (InterruptedException ie) { log("error : "+ ie); } //退出程序 android.os.Process.killProcess(android.os.Process.myPid()); System.exit(1); } } /** * 处理异常 * @param e * @return */ private boolean handleException(Throwable e) { if (e == null) return false; new Thread() { @Override public void run() { Looper.prepare(); Toast.makeText(mContext, "程序出错退出", Toast.LENGTH_LONG).show(); Looper.loop(); } }.start(); collectDeviceInfo(); saveCrashInfo(e); return true; } /** * 收集设备参数信息 */ private void collectDeviceInfo() { try { PackageManager pm = mContext.getPackageManager();// 获得包管理器 PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);// 得到该应用的信息,即主Activity if (pi != null) { String versionName = pi.versionName == null ? "null" : pi.versionName; String versionCode = pi.versionCode + ""; infoMap.put("versionName", versionName); infoMap.put("versionCode", versionCode); } //遍历所有Build中的字段 Field[] fields = Build.class.getDeclaredFields(); for (Field field : fields){ infoMap.put(field.getName(),field.get(null).toString()); log(field.getName()+" : "+field.get(null).toString()); } } catch (Exception e) { log(e.getMessage()); } } /** * 保存日志到SDk * @param ex */ private void saveCrashInfo(Throwable ex){ StringBuilder sb = new StringBuilder(); sb.append("******************** 手机设备信息 ********************" + "\r\n"); for (Map.Entry<String,String> entry : infoMap.entrySet()){ String key = entry.getKey(); String value = entry.getValue(); sb.append(key + " = " + value + "\r\n"); } Writer writer = new StringWriter(); PrintWriter pw = new PrintWriter(writer); ex.printStackTrace(pw); Throwable cause = ex.getCause(); // 循环着把所有的异常信息写入writer中 while (cause != null) { cause.printStackTrace(pw); cause = cause.getCause(); } pw.close();// 记得关闭 String result = writer.toString(); sb.append("******************** 发生崩溃异常信息 ********************" + "\r\n"); sb.append(result); log(result); try { long timestamp = System.currentTimeMillis(); DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); String time = formatter.format(new Date()); String fileName = "crash-" + time + "-"+ timestamp + ".log"; if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ String path = "/sdcard/crash/yyf/"; File dir = new File(path); if (!dir.exists()){ dir.mkdirs(); } FileOutputStream fos = new FileOutputStream(path + fileName); fos.write(sb.toString().getBytes()); fos.close(); } }catch (Exception e){ Log.e(TAG,"an error occured while writing file...",e); } } private void log(String msg){ Log.e(CrashHandler.class.getSimpleName(),msg); } }
Util工具写完,那么说一下我在Application中的处理
1、定义一下debug判断
private static Boolean mDebug = null; public static boolean isDebug(){ return mDebug == null ? false:mDebug.booleanValue(); } private static void syncDebug(Context context){ if (mDebug == null){ mDebug = context.getApplicationInfo() != null && (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; } }
2、然后就可以直接在Application的onCreate()方法中调用我们的CrashHandler捕获异常工具类啦
if (isDebug()) {//debug情况下使用 //系统错误捕获 CrashHandler.getInstance(this).init(); }ok这样就写完了,出现异常崩溃去手机的SD卡找日志分析问题去吧