Android热修复之 - 收集崩溃信息上传服务器

1.概述


开始想收集崩溃信息是因为测试的哥们老是说崩了,但是一过来就开始拍脑袋说 我*怎么好了?所以后来上网查了很多信息,找到了一种方法。大致的流程就是在用户崩溃的时候,我们获取崩溃信息、应用当前的信息和手机信息,然后把它保存到手机内存卡,再找我就直接找出来看看。后来衍生到上线后某些奇葩机型会有部分问题,所以不得不上传到服务器,后来发现居然可以配合热修复一步一步如此神奇,接下来我们来玩一玩,如何才能把用户的崩溃信息上传到服务器。大家也可以去找腾讯他有现成的:https://bugly.qq.com/v2/index 友盟也有现成的:http://www.umeng.com/ 实现的原理都类似。

视频讲解:https://pan.baidu.com/s/1nwlqeA9

相关文章:

2017Android进阶之路与你同行
  
Android热修复之 - 收集崩溃信息上传至服务器

Android热修复之 - 打补丁原来如此简单

GIF.gif

2.实现


2.1 拦截闪退信息
  
  如何去收集我们的闪退信息?我们需要认识一下这个类Thread.UncaughtExceptionHandler,一言不和就看源码,这个可以不看,且看我是如何写的。

1/** 2 * Created by Darren on 2017/2/8. 3 * Email: 240336124@qq.com 4 * Description: 拦截应用的闪退信息 5 */ 6public class ExceptionCrashHandler implements Thread.UncaughtExceptionHandler { 7 8 private static final String TAG = "ExceptionCrashHandler"; 9 // 单例设计模式 10 private static ExceptionCrashHandler mInstance; 11 // 留下原来的,便于开发的时候调试 12 private Thread.UncaughtExceptionHandler mDefaultHandler; 13 // 上下文 获取版本信息和手机信息 14 private Context mContext; 15 16 public static ExceptionCrashHandler getInstance() { 17 if (mInstance == null) { 18 synchronized (ExceptionCrashHandler.class) { 19 if (mInstance == null) { 20 mInstance = new ExceptionCrashHandler(); 21 } 22 } 23 } 24 return mInstance; 25 } 26 27 private ExceptionCrashHandler() { 28 29 } 30 31 public void init(Context context) { 32 /** 33 * 官方解释 34 * Set the handler invoked when this thread abruptly terminates 35 * due to an uncaught exception. 36 **/ 37 Thread.currentThread().setUncaughtExceptionHandler(this); 38 // 获取系统默认的UncaughtException处理器 39 mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); 40 this.mContext = context; 41 } 42 43 @Override 44 public void uncaughtException(Thread t, Throwable ex) { 45 Log.e(TAG, "到拦截闪退信息"); 46 } 47 48}

在Application的onCreate()中配置一下,然后在任何一个地方写一个异常试一试:

1/** 2 * Created by Darren on 2017/2/8. 3 * Email: 240336124@qq.com 4 * Description: BaseApplication 5 */ 6public class BaseApplication extends Application { 7 @Override 8 public void onCreate() { 9 super.onCreate(); 10 ExceptionCrashHandler.getInstance().init(this); 11 } 12}

2.2 收集闪退信息

这样每次崩溃的时候都会进入uncaughtException(),这个时候我们只需要收集信息写入本地文件就好了,收集的信息肯定需要包含好几个部分:当前崩溃信息,当前应用的版本信息,当前手机的信息,有的时候我们还需要其他部分,这里大概就只收集这三部分。为什么收集收集手机信息呢?因为有的时候是由于某些特定手机引起的Bug,若怪罪下来的话我们要甩锅给他。

1 @Override 2 public void uncaughtException(Thread t, Throwable ex) { 3 Log.e(TAG, "捕捉到了异常"); 4 // 1. 获取信息 5 // 1.1 崩溃信息 6 // 1.2 手机信息 7 // 1.3 版本信息 8 // 2.写入文件 9 String crashFileName = saveInfoToSD(ex); 10 11 Log.e(TAG, "fileName --> " + crashFileName); 12 13 // 3. 缓存崩溃日志文件 14 cacheCrashFile(crashFileName); 15 // 系统默认处理 16 mDefaultHandler.uncaughtException(t, ex); 17 } 18 19 /** 20 * 缓存崩溃日志文件 21 * 22 * @param fileName 23 */ 24 private void cacheCrashFile(String fileName) { 25 SharedPreferences sp = mContext.getSharedPreferences("crash", Context.MODE_PRIVATE); 26 sp.edit().putString("CRASH_FILE_NAME", fileName).commit(); 27 } 28 29 30 /** 31 * 获取崩溃文件名称 32 * 33 * @return 34 */ 35 public File getCrashFile() { 36 String crashFileName = mContext.getSharedPreferences("crash", 37 Context.MODE_PRIVATE).getString("CRASH_FILE_NAME", ""); 38 return new File(crashFileName); 39 } 40 41 /** 42 * 保存获取的 软件信息,设备信息和出错信息保存在SDcard中 43 * 44 * @param ex 45 * @return 46 */ 47 private String saveInfoToSD(Throwable ex) { 48 String fileName = null; 49 StringBuffer sb = new StringBuffer(); 50 51 for (Map.Entry<String, String> entry : obtainSimpleInfo(mContext) 52 .entrySet()) { 53 String key = entry.getKey(); 54 String value = entry.getValue(); 55 sb.append(key).append(" = ").append(value).append("\n"); 56 } 57 58 sb.append(obtainExceptionInfo(ex)); 59 60 if (Environment.getExternalStorageState().equals( 61 Environment.MEDIA_MOUNTED)) { 62 File dir = new File(mContext.getFilesDir() + File.separator + "crash" 63 + File.separator); 64 65 // 先删除之前的异常信息 66 if (dir.exists()) { 67 deleteDir(dir); 68 } 69 70 // 再从新创建文件夹 71 if (!dir.exists()) { 72 dir.mkdir(); 73 } 74 try { 75 fileName = dir.toString() 76 + File.separator 77 + getAssignTime("yyyy_MM_dd_HH_mm") + ".txt"; 78 FileOutputStream fos = new FileOutputStream(fileName); 79 fos.write(sb.toString().getBytes()); 80 fos.flush(); 81 fos.close(); 82 } catch (Exception e) { 83 e.printStackTrace(); 84 } 85 } 86 return fileName; 87 } 88 89 /** 90 * 返回当前日期根据格式 91 **/ 92 private String getAssignTime(String dateFormatStr) { 93 DateFormat dataFormat = new SimpleDateFormat(dateFormatStr); 94 long currentTime = System.currentTimeMillis(); 95 return dataFormat.format(currentTime); 96 } 97 98 99 /** 100 * 获取一些简单的信息,软件版本,手机版本,型号等信息存放在HashMap中 101 * 102 * @return 103 */ 104 private HashMap<String, String> obtainSimpleInfo(Context context) { 105 HashMap<String, String> map = new HashMap<>(); 106 PackageManager mPackageManager = context.getPackageManager(); 107 PackageInfo mPackageInfo = null; 108 try { 109 mPackageInfo = mPackageManager.getPackageInfo( 110 context.getPackageName(), PackageManager.GET_ACTIVITIES); 111 } catch (PackageManager.NameNotFoundException e) { 112 e.printStackTrace(); 113 } 114 map.put("versionName", mPackageInfo.versionName); 115 map.put("versionCode", "" + mPackageInfo.versionCode); 116 map.put("MODEL", "" + Build.MODEL); 117 map.put("SDK_INT", "" + Build.VERSION.SDK_INT); 118 map.put("PRODUCT", "" + Build.PRODUCT); 119 map.put("MOBLE_INFO", getMobileInfo()); 120 return map; 121 } 122 123 124 /** 125 * Cell phone information 126 * 127 * @return 128 */ 129 public static String getMobileInfo() { 130 StringBuffer sb = new StringBuffer(); 131 try { 132 Field[] fields = Build.class.getDeclaredFields(); 133 for (Field field : fields) { 134 field.setAccessible(true); 135 String name = field.getName(); 136 String value = field.get(null).toString(); 137 sb.append(name + "=" + value); 138 sb.append("\n"); 139 } 140 } catch (Exception e) { 141 e.printStackTrace(); 142 } 143 return sb.toString(); 144 } 145 146 147 /** 148 * 获取系统未捕捉的错误信息 149 * 150 * @param throwable 151 * @return 152 */ 153 private String obtainExceptionInfo(Throwable throwable) { 154 StringWriter stringWriter = new StringWriter(); 155 PrintWriter printWriter = new PrintWriter(stringWriter); 156 throwable.printStackTrace(printWriter); 157 printWriter.close(); 158 return stringWriter.toString(); 159 } 160 161 162 /** 163 * 递归删除目录下的所有文件及子目录下所有文件 164 * 165 * @param dir 将要删除的文件目录 166 * @return boolean Returns "true" if all deletions were successful. If a 167 * deletion fails, the method stops attempting to delete and returns 168 * "false". 169 */ 170 private boolean deleteDir(File dir) { 171 if (dir.isDirectory()) { 172 String[] children = dir.list(); 173 // 递归删除目录中的子目录下 174 for (int i = 0; i < children.length; i++) { 175 boolean success = deleteDir(new File(dir, children[i])); 176 if (!success) { 177 return false; 178 } 179 } 180 } 181 // 目录此时为空,可以删除 182 return true; 183 }

保存的路径最好不要在用户的外部存储卡中,因为6.0的时候如果访问外部存储卡需要动态的申请权限,那这个时候信息是获取到了但是GG。都蹦了还拖着我不放,还需要申请权限,纳尼???

2.2 上传闪退信息

每次启动应用的时候就获取上次闪退的信息日志,然后上传到服务器。上传就先不上传了,等内涵段子的系统架构和功能都完毕后我们再来上传,然后打包但是不能上线,要不然要背官司的。

1/** 2 * Created by Darren on 2017/2/8. 3 * Email: 240336124@qq.com 4 * Description: 主页面MainActivity 5 */ 6public class MainActivity extends BaseActivity { 7 8 @Override 9 protected void initData() { 10 // 获取上次的崩溃信息 11 File crashFile = ExceptionCrashHandler.getInstance().getCrashFile(); 12 // 上传到服务器,后面再说....... 13 } 14 15 @Override 16 protected void initView() { 17 18 } 19 20 @Override 21 protected void setContentView() { 22 setContentView(R.layout.activity_main); 23 } 24 25 @Override 26 protected void initTitle() { 27 28 } 29}

视频讲解:https://pan.baidu.com/s/1nwlqeA9

相关文章:

2017Android进阶之路与你同行
  
Android热修复之 - 收集崩溃信息上传至服务器

Android热修复之 - 打补丁原来如此简单

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

java爬虫

想找一些图片做桌面背景,但是又不想一张张去下载,后来就想到了爬虫。。。对于爬虫我也没具体用过,在网上一顿搜索后写了个小demo。爬虫的具体思路就是:1.调用url爬取网页信息2.解析网页信息3.保存数据刚开始还用正则去匹配,获取img标签中的src地址,但是发现有很多不便(主要我正则不太会),后来发现了jsoup这个神器。jsoup

Android Native crash 处理案例分享

1\.背景目前mPaas\1\Android使用CrashSDK对闪退进行的处理,CrashSDK是Android平台上一款功能强大的崩溃日志收集SDK,有着极高的崩溃收集率和完整、全面的崩溃日志信息,生成的日志内容非常利于问题的跟进和解决。在日常运维中,经常遇到一些闪退,无法直接从闪退堆栈找到原因,尤其是一些非Java

Twitter的分布式自增ID算法snowflake (Java版)

概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移