Android热修复之 - 收集崩溃信息上传服务器
1.概述
大致的流程就是在用戶崩潰的時候,我們獲取崩潰信息、應(yīng)用當(dāng)前的信息和手機(jī)信息,然后把它保存到手機(jī)內(nèi)存卡,再找我就直接找出來看看。后來衍生到上線后某些奇葩機(jī)型會有部分問題,所以不得不上傳到服務(wù)器,后來發(fā)現(xiàn)居然可以配合熱修復(fù)一步一步如此神奇,接下來我們來玩一玩,如何才能把用戶的崩潰信息上傳到服務(wù)器。大家也可以去找騰訊他有現(xiàn)成的:https://bugly.qq.com/v2/index 友盟也有現(xiàn)成的:http://www.umeng.com/ 實現(xiàn)的原理都類似。2.實現(xiàn)
2.1 攔截閃退信息
如何去收集我們的閃退信息?我們需要認(rèn)識一下這個類Thread.UncaughtExceptionHandler,一言不和就看源碼,這個可以不看,且看我是如何寫的。
攔截應(yīng)用的閃退信息
1 public class ExceptionCrashHandler implements Thread.UncaughtExceptionHandler { 2 3 private static final String TAG = "ExceptionCrashHandler"; 4 // 單例設(shè)計模式 5 private static ExceptionCrashHandler mInstance; 6 // 留下原來的,便于開發(fā)的時候調(diào)試 7 private Thread.UncaughtExceptionHandler mDefaultHandler; 8 // 上下文 獲取版本信息和手機(jī)信息 9 private Context mContext; 10 11 public static ExceptionCrashHandler getInstance() { 12 if (mInstance == null) { 13 synchronized (ExceptionCrashHandler.class) { 14 if (mInstance == null) { 15 mInstance = new ExceptionCrashHandler(); 16 } 17 } 18 } 19 return mInstance; 20 } 21 22 private ExceptionCrashHandler() { 23 24 } 25 26 public void init(Context context) { 27 /** 28 * 官方解釋 29 * Set the handler invoked when this thread abruptly terminates 30 * due to an uncaught exception. 31 **/ 32 Thread.currentThread().setUncaughtExceptionHandler(this); 33 // 獲取系統(tǒng)默認(rèn)的UncaughtException處理器 34 mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); 35 this.mContext = context; 36 } 37 38 @Override 39 public void uncaughtException(Thread t, Throwable ex) { 40 Log.e(TAG, "到攔截閃退信息"); 41 } 42 43 }在Application的onCreate()中配置一下,然后在任何一個地方寫一個異常試一試:
public class BaseApplication extends Application {@Overridepublic void onCreate() {super.onCreate();ExceptionCrashHandler.getInstance().init(this);} }2.2 收集閃退信息
這樣每次崩潰的時候都會進(jìn)入uncaughtException(),這個時候我們只需要收集信息寫入本地文件就好了,收集的信息肯定需要包含好幾個部分:當(dāng)前崩潰信息,當(dāng)前應(yīng)用的版本信息,當(dāng)前手機(jī)的信息,有的時候我們還需要其他部分,這里大概就只收集這三部分。為什么收集收集手機(jī)信息呢?因為有的時候是由于某些特定手機(jī)引起的Bug,若怪罪下來的話我們要甩鍋給他。
1 @Override 2 public void uncaughtException(Thread t, Throwable ex) { 3 Log.e(TAG, "捕捉到了異常"); 4 // 1. 獲取信息 5 // 1.1 崩潰信息 6 // 1.2 手機(jī)信息 7 // 1.3 版本信息 8 // 2.寫入文件 9 String crashFileName = saveInfoToSD(ex); 10 11 Log.e(TAG, "fileName --> " + crashFileName); 12 13 // 3. 緩存崩潰日志文件 14 cacheCrashFile(crashFileName); 15 // 系統(tǒng)默認(rèn)處理 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 * 保存獲取的 軟件信息,設(shè)備信息和出錯信息保存在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 // 再從新創(chuàng)建文件夾 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 * 返回當(dāng)前日期根據(jù)格式 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 * 獲取一些簡單的信息,軟件版本,手機(jī)版本,型號等信息存放在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 * 獲取系統(tǒng)未捕捉的錯誤信息 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的時候如果訪問外部存儲卡需要動態(tài)的申請權(quán)限,那這個時候信息是獲取到了但是GG。都蹦了還拖著我不放,還需要申請權(quán)限,納尼???
2.2 上傳閃退信息
每次啟動應(yīng)用的時候就獲取上次閃退的信息日志,然后上傳到服務(wù)器。
public class MainActivity extends BaseActivity {@Overrideprotected void initData() {// 獲取上次的崩潰信息File crashFile = ExceptionCrashHandler.getInstance().getCrashFile();// 上傳到服務(wù)器,后面再說....... }@Overrideprotected void initView() {}@Overrideprotected void setContentView() {setContentView(R.layout.activity_main);}@Overrideprotected void initTitle() {} }?
http://www.cnblogs.com/ganchuanpu/p/8196771.html
總結(jié)
以上是生活随笔為你收集整理的Android热修复之 - 收集崩溃信息上传服务器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android热修复之 - 阿里开源的热
- 下一篇: 阿里SopHix热修复框架