編輯:關於Android編程
應用發生crash之後要查看log,判斷問題出在什麼地方,可是一旦應用發布出去,就要想辦法把用戶的崩潰日志拿到分析。
所以要在發生crash之後抓取log,然後上傳到服務器,方便開發者查看,現在都有很多第三方做這方面的服務,這裡說下如何自己來實現。
其實原理很簡單,應用出現異常後,會由默認的異常處理器來處理異常,
我們要做的就是把這個任務接管過來,自己處理異常,包括收集日志,保存到本地,然後上傳到服務器。
下面是自己實現的異常處理類。
public class CrashHandler implements UncaughtExceptionHandler { public static final String TAG = CrashHandler; // 系統默認的UncaughtException處理類 private Thread.UncaughtExceptionHandler mDefaultHandler; // CrashHandler實例 private static CrashHandler INSTANCE = new CrashHandler(); // 程序的Context對象 private Context mContext; // 用來存儲設備信息和異常信息 private Mapinfos = new HashMap (); // 用於格式化日期,作為日志文件名的一部分 private DateFormat formatter = new SimpleDateFormat(yyyy-MM-dd_HH-mm-ss); private String nameString; /** 保證只有一個CrashHandler實例 */ private CrashHandler() { } /** 獲取CrashHandler實例 ,單例模式 */ public static CrashHandler getInstance() { return INSTANCE; } /** * 初始化 * * @param context */ public void init(Context context) { mContext = context; // 獲取系統默認的UncaughtException處理器 mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); // 設置該CrashHandler為程序的默認處理器 Thread.setDefaultUncaughtExceptionHandler(this); nameString = BmobUserManager.getInstance(mContext).getCurrentUserName(); } /** * 當UncaughtException發生時會轉入該函數來處理 */ @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) { Log.e(TAG, error : , e); } // 退出程序 android.os.Process.killProcess(android.os.Process.myPid()); System.exit(1); } } /** * 自定義錯誤處理,收集錯誤信息 發送錯誤報告等操作均在此完成. * * @param ex * @return true:如果處理了該異常信息;否則返回false. */ private boolean handleException(Throwable ex) { if (ex == null) { return false; } WonderMapApplication.getInstance().getSpUtil().setCrashLog(true);// 每次進入應用檢查,是否有log,有則上傳 // 使用Toast來顯示異常信息 new Thread() { @Override public void run() { Looper.prepare(); Toast.makeText(mContext, 很抱歉,程序出現異常,正在收集日志,即將退出, Toast.LENGTH_LONG) .show(); Looper.loop(); } }.start(); // 收集設備參數信息 collectDeviceInfo(mContext); // 保存日志文件 String fileName = saveCrashInfo2File(ex); return true; } /** * 收集設備參數信息 * * @param ctx */ 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) { Log.e(TAG, an 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()); Log.d(TAG, field.getName() + : + field.get(null)); } catch (Exception e) { Log.e(TAG, an error occured when collect crash info, e); } } } /** * 保存錯誤信息到文件中 * * @param ex * @return 返回文件名稱,便於將文件傳送到服務器 */ private String saveCrashInfo2File(Throwable ex) { StringBuffer sb = new StringBuffer(); for (Map.Entry entry : infos.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); sb.append(key + = + value + ); } 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(); L.d(WModel.CrashUpload, result); sb.append(result); try { long timestamp = System.currentTimeMillis(); String time = formatter.format(new Date()); String fileName = nameString + - + time + - + timestamp + .log; if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { String path = WMapConstants.CrashLogDir; File dir = new File(path); if (!dir.exists()) { dir.mkdirs(); } FileOutputStream fos = new FileOutputStream(path + fileName); fos.write(sb.toString().getBytes()); fos.close(); } return fileName; } catch (Exception e) { Log.e(TAG, an error occured while writing file..., e); } return null; } }
在Application的onCreate中加上下面
CrashHandler crashHandler = CrashHandler.getInstance(); crashHandler.init(this);
其實這樣還有一個好處,就是不用在logcat裡面翻來翻去找日志,直接到本地文件夾打開看就是了。
SQLite是Android平台軟件開發中會經常用到的數據庫產品,作為一款輕型數據庫,SQLite的設計目標就是是嵌入式的,而且目前已經在很多嵌入式產品中使用了它,它占用
我用的是cocos2d-2.0-x-2.0.3 之前弄了一天也沒成功 今天來了下載了最新的ndk8 更新了sdk 又重新是了一遍 居然成功了,不知道是工具的版本問題還是哪
今天主要使用這個庫來實現RecyclerView 的左右滑動item 和 拖拽排序Gradle配置compile 'com.android.suppo
理解球坐標系首先看下球的坐標系 ,如圖 : (圖來自百度百科 ) 設球上有一點 A ,球心為O ,OA在 xOy上的投影與X軸夾角為φ (范圍