获取Android崩溃crash信息并写入日志
2016-12-14 17:45
197 查看
Android崩溃是开发中不可避免的一件事,考虑不够周全的代码、糟糕的网络环境、让人头疼的碎片化问题都可能导致crash,线上版本crash严重影响用户体验,所以crash的捕获和收集对我们开发人员很重要。
〇、Exception的分类及捕获
Java的异常可以分为两类:
UnChecked ExceptionChecked Exception
所有RuntimeException类及其子类的实例被称为Runtime异常,即UnChecked Exception,
不是RuntimeException类及其子类的异常实例则被称为Checked Exception。Checked异常又称为编译时异常,即在编译阶段被处理的异常。编译器会强制程序处理所有的Checked异常,也就是用try…catch显式的捕获并处理,因为Java认为这类异常都是可以被处理(修复)的。在Java API文档中,方法说明时,都会添加是否throw某个exception,这个exception就是Checked异常。如果没有try…catch这个异常,则编译出错,错误提示类似于“Unhandled
exception type xxxxx ...”and so on .
一、实现Thread.UncaughtExceptionHandler
UnChecked异常发生时,由于没有相应的try…catch处理该异常对象,所以Java运行环境将会终止,程序将退出,也就是我们所说的Crash。Java API提供了一个全局异常捕获处理器,Android应用在Java层捕获Crash依赖的就是Thread.UncaughtExceptionHandler处理器接口,通常我们只需实现这个接口,并重写其中的uncaughtException方法,在该方法中可以读取Crash的堆栈信息。
public class CrashManager implements Thread.UncaughtExceptionHandler { private Thread.UncaughtExceptionHandler mDefaultHandler; private Map<String, String> infos; private MyApplication application; public CrashManager(MyApplication application){ //获取系统默认的UncaughtExceptionHandler mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); this.application = application; } private boolean handleException(final Throwable exc){ if (exc == null) { return false; } new Thread(new Runnable() { @Override public void run() { Looper.prepare(); Log.i("Urmytch","崩溃正在写入日志"); flushBufferedUrlsAndReturn(); //处理崩溃 collectDeviceAndUserInfo(application); writeCrash(exc); Looper.loop(); } }).start(); return true; } /** * 把未存盘的url和返回数据写入日志文件 */ private void flushBufferedUrlsAndReturn(){ //TODO 可以在请求网络时把url和返回xml或json数据缓存在队列中,崩溃时先写入以便查明原因 } /** * 采集设备和用户信息 * @param context 上下文 */ private void collectDeviceAndUserInfo(Context context){ PackageManager pm = context.getPackageManager(); infos = new HashMap<String, String>(); try { PackageInfo pi = pm.getPackageInfo(context.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); infos.put("crashTime",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); } } catch (PackageManager.NameNotFoundException e) { Log.e("Urmytch",e.getMessage()); } Field[] fields = Build.class.getDeclaredFields(); try { for (Field field : fields) { field.setAccessible(true); infos.put(field.getName(), field.get(null).toString()); } } catch (IllegalAccessException e) { Log.e("Urmytch",e.getMessage()); } } /** * 采集崩溃原因 * @param exc 异常 */ private void writeCrash(Throwable exc){ StringBuffer sb = new StringBuffer(); sb.append("------------------crash----------------------"); sb.append("\r\n"); for (Map.Entry<String,String> entry : infos.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); sb.append(key+"="+value+"\r\n"); } Writer writer = new StringWriter(); PrintWriter pw = new PrintWriter(writer); exc.printStackTrace(pw); Throwable excCause = exc.getCause(); while (excCause != null) { excCause.printStackTrace(pw); excCause = excCause.getCause(); } pw.close(); String result = writer.toString(); sb.append(result); sb.append("\r\n"); sb.append("-------------------end-----------------------"); sb.append("\r\n"); if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { String sdcardPath = Environment.getExternalStorageDirectory().getPath(); String filePath = sdcardPath + "//Urmytch/crash/"; writeLog(sb.toString(), filePath); } } /** * * @param log 文件内容 * @param name 文件路径 * @return 返回写入的文件路径 * 写入Log信息的方法,写入到SD卡里面 */ private String writeLog(String log, String name) { String filename = name + "mycrash"+ ".log"; File file =new File(filename); if(!file.getParentFile().exists()){ Log.i("Urmytch","新建文件"); file.getParentFile().mkdirs(); } if (file != null && file.exists() && file.length() + log.length() >= 64 * 1024) { //控制日志文件大小 file.delete(); } try { file.createNewFile(); FileWriter fw=new FileWriter(file,true); BufferedWriter bw = new BufferedWriter(fw); //写入相关Log到文件 bw.write(log); bw.newLine(); bw.close(); fw.close(); return filename; } catch(IOException e) { Log.w("Urmytch",e.getMessage()); return null; } } @Override public void uncaughtException(Thread thread, Throwable exc) { if(!handleException(exc) && mDefaultHandler != null){ //如果用户没有处理则让系统默认的异常处理器来处理 mDefaultHandler.uncaughtException(thread, exc); }else{ try{ Thread.sleep(2000); }catch (InterruptedException e){ Log.w("Urmytch",e.getMessage()); } Intent intent = new Intent(application.getApplicationContext(), MainActivity.class); PendingIntent restartIntent = PendingIntent.getActivity( application.getApplicationContext(), 0, intent, 0); //退出程序 AlarmManager mgr = (AlarmManager)application.getSystemService(Context.ALARM_SERVICE); //1秒后重启应用 mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, restartIntent); android.os.Process.killProcess(android.os.Process.myPid()); } } }
二、在Application中注册
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); CrashManager crashHandler = new CrashManager(this); Thread.setDefaultUncaughtExceptionHandler(crashHandler); } }
三、看下效果
点击button会直接crash掉
四、与友盟错误统计是否会存在冲突
友盟是这样说的:开发者自己使用UncaughtExceptionHandler在程序中添加了全局的异常捕捉时,如果是开发者的先注册友盟的后注册,友盟不会覆盖开发者的,但是如果友盟先注册,开发者注册的可能会覆盖友盟的。如果您不需要错误统计,可以通过MobclickAgent.setCatchUncaughtExceptions(false);关闭,如果开发者需要自己上传错误,友盟也提供相应的方法:public static void reportError(Context
context, String error) //或public static void reportError(Context context, Throwable e)。
经验证本demo中并未冲突,本地捕获并记录crash信息(重启应用后友盟才会提交崩溃数据)后友盟依然记录到了崩溃
相关文章推荐
- Android应用如何反馈Crash报告(即上传崩溃日志信息)
- Android log日志信息获取
- Android软件开发之在程序中时时获取logcat日志信息
- Android崩溃日志获取与解析
- android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地
- Android应用崩溃后异常捕获并重启并写入日志
- android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地
- Android log日志信息获取
- android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地
- Android开发中获取crash信息
- Android软件开发之在程序中时时获取logcat日志信息(三十三)
- Android和IOS获取crash信息
- Android软件开发之在程序中时时获取logcat日志信息(三十三)
- Android开发之在程序中时时获取logcat日志信息的方法(附demo源码下载)
- Android软件开发之在程序中时时获取logcat日志信息(三十三)
- android开发之应用Crash自动抓取Log_自动保存崩溃日志到本地
- cocos2dx获取Android异常崩溃堆栈信息
- app被拒,官方提供了crash日志,需要解析获取到崩溃的具体位置(一)
- android 程序崩溃crash日志的捕捉
- 获取IOS应用异常崩溃日志信息