1.概述
开始想收集崩溃信息是因为测试的哥们老是说崩了,但是一过来就开始拍脑袋说 我*怎么好了?所以后来上网查了很多信息,找到了一种方法。大致的流程就是在用户崩溃的时候,我们获取崩溃信息、应用当前的信息和手机信息,然后把它保存到手机内存卡,再找我就直接找出来看看。后来衍生到上线后某些奇葩机型会有部分问题,所以不得不上传到服务器,后来发现居然可以配合热修复一步一步如此神奇,接下来我们来玩一玩,如何才能把用户的崩溃信息上传到服务器。大家也可以去找腾讯他有现成的:https://bugly.qq.com/v2/index 友盟也有现成的:http://www.umeng.com/ 实现的原理都类似。
视频讲解:http://pan.baidu.com/s/1i5woOtR
相关文章:
2.实现
2.1 拦截闪退信息 如何去收集我们的闪退信息?我们需要认识一下这个类Thread.UncaughtExceptionHandler,一言不和就看源码,这个可以不看,且看我是如何写的。
/** * Created by Darren on 2017/2/8. * Email: 240336124@qq.com * Description: 拦截应用的闪退信息 */public class ExceptionCrashHandler implements Thread.UncaughtExceptionHandler { private static final String TAG = "ExceptionCrashHandler"; // 单例设计模式 private static ExceptionCrashHandler mInstance; // 留下原来的,便于开发的时候调试 private Thread.UncaughtExceptionHandler mDefaultHandler; // 上下文 获取版本信息和手机信息 private Context mContext; public static ExceptionCrashHandler getInstance() { if (mInstance == null) { synchronized (ExceptionCrashHandler.class) { if (mInstance == null) { mInstance = new ExceptionCrashHandler(); } } } return mInstance; } private ExceptionCrashHandler() { } public void init(Context context) { /** * 官方解释 * Set the handler invoked when this thread abruptly terminates * due to an uncaught exception. **/ Thread.currentThread().setUncaughtExceptionHandler(this); // 获取系统默认的UncaughtException处理器 mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); this.mContext = context; } @Override public void uncaughtException(Thread t, Throwable ex) { Log.e(TAG, "到拦截闪退信息"); }}复制代码
在Application的onCreate()中配置一下,然后在任何一个地方写一个异常试一试:
/** * Created by Darren on 2017/2/8. * Email: 240336124@qq.com * Description: BaseApplication */public class BaseApplication extends Application { @Override public void onCreate() { super.onCreate(); ExceptionCrashHandler.getInstance().init(this); }}复制代码
2.2 收集闪退信息
这样每次崩溃的时候都会进入uncaughtException(),这个时候我们只需要收集信息写入本地文件就好了,收集的信息肯定需要包含好几个部分:当前崩溃信息,当前应用的版本信息,当前手机的信息,有的时候我们还需要其他部分,这里大概就只收集这三部分。为什么收集收集手机信息呢?因为有的时候是由于某些特定手机引起的Bug,若怪罪下来的话我们要甩锅给他。
@Override public void uncaughtException(Thread t, Throwable ex) { Log.e(TAG, "捕捉到了异常"); // 1. 获取信息 // 1.1 崩溃信息 // 1.2 手机信息 // 1.3 版本信息 // 2.写入文件 String crashFileName = saveInfoToSD(ex); Log.e(TAG, "fileName --> " + crashFileName); // 3. 缓存崩溃日志文件 cacheCrashFile(crashFileName); // 系统默认处理 mDefaultHandler.uncaughtException(t, ex); } /** * 缓存崩溃日志文件 * * @param fileName */ private void cacheCrashFile(String fileName) { SharedPreferences sp = mContext.getSharedPreferences("crash", Context.MODE_PRIVATE); sp.edit().putString("CRASH_FILE_NAME", fileName).commit(); } /** * 获取崩溃文件名称 * * @return */ public File getCrashFile() { String crashFileName = mContext.getSharedPreferences("crash", Context.MODE_PRIVATE).getString("CRASH_FILE_NAME", ""); return new File(crashFileName); } /** * 保存获取的 软件信息,设备信息和出错信息保存在SDcard中 * * @param ex * @return */ private String saveInfoToSD(Throwable ex) { String fileName = null; StringBuffer sb = new StringBuffer(); for (Map.Entryentry : obtainSimpleInfo(mContext) .entrySet()) { String key = entry.getKey(); String value = entry.getValue(); sb.append(key).append(" = ").append(value).append("\n"); } sb.append(obtainExceptionInfo(ex)); if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { File dir = new File(mContext.getFilesDir() + File.separator + "crash" + File.separator); // 先删除之前的异常信息 if (dir.exists()) { deleteDir(dir); } // 再从新创建文件夹 if (!dir.exists()) { dir.mkdir(); } try { fileName = dir.toString() + File.separator + getAssignTime("yyyy_MM_dd_HH_mm") + ".txt"; FileOutputStream fos = new FileOutputStream(fileName); fos.write(sb.toString().getBytes()); fos.flush(); fos.close(); } catch (Exception e) { e.printStackTrace(); } } return fileName; } /** * 返回当前日期根据格式 **/ private String getAssignTime(String dateFormatStr) { DateFormat dataFormat = new SimpleDateFormat(dateFormatStr); long currentTime = System.currentTimeMillis(); return dataFormat.format(currentTime); } /** * 获取一些简单的信息,软件版本,手机版本,型号等信息存放在HashMap中 * * @return */ private HashMap obtainSimpleInfo(Context context) { HashMap map = new HashMap<>(); 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); map.put("MOBLE_INFO", getMobileInfo()); return map; } /** * Cell phone information * * @return */ public static String getMobileInfo() { StringBuffer sb = new StringBuffer(); try { Field[] fields = Build.class.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); String name = field.getName(); String value = field.get(null).toString(); sb.append(name + "=" + value); sb.append("\n"); } } catch (Exception e) { e.printStackTrace(); } return sb.toString(); } /** * 获取系统未捕捉的错误信息 * * @param throwable * @return */ private String obtainExceptionInfo(Throwable throwable) { StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter); throwable.printStackTrace(printWriter); printWriter.close(); return stringWriter.toString(); } /** * 递归删除目录下的所有文件及子目录下所有文件 * * @param dir 将要删除的文件目录 * @return boolean Returns "true" if all deletions were successful. If a * deletion fails, the method stops attempting to delete and returns * "false". */ private boolean deleteDir(File dir) { if (dir.isDirectory()) { String[] children = dir.list(); // 递归删除目录中的子目录下 for (int i = 0; i < children.length; i++) { boolean success = deleteDir(new File(dir, children[i])); if (!success) { return false; } } } // 目录此时为空,可以删除 return true; }复制代码
保存的路径最好不要在用户的外部存储卡中,因为6.0的时候如果访问外部存储卡需要动态的申请权限,那这个时候信息是获取到了但是GG。都蹦了还拖着我不放,还需要申请权限,纳尼???
2.2 上传闪退信息
每次启动应用的时候就获取上次闪退的信息日志,然后上传到服务器。上传就先不上传了,等内涵段子的系统架构和功能都完毕后我们再来上传,然后打包但是不能上线,要不然要背官司的。
/** * Created by Darren on 2017/2/8. * Email: 240336124@qq.com * Description: 主页面MainActivity */public class MainActivity extends BaseActivity { @Override protected void initData() { // 获取上次的崩溃信息 File crashFile = ExceptionCrashHandler.getInstance().getCrashFile(); // 上传到服务器,后面再说....... } @Override protected void initView() { } @Override protected void setContentView() { setContentView(R.layout.activity_main); } @Override protected void initTitle() { }}复制代码
视频讲解:http://pan.baidu.com/s/1i5woOtR
相关文章: