使用CrashHandler来获取应用的oom crash信息
2017-02-21 11:17
477 查看
GitHub源码地址
Android应用开发中不可避免地会发生崩溃,特别是在用户使用过程中,一些特定场景的偶然概率的crash会通常让开发者抓狂。幸运的是Android提供了处理这类问题的方法,当App Crash时,我们可以记录下Crash的原因或者是一些设备信息,并上传到服务器供开发者分析,以便开发者迅速定位问题原因。
实现这个功能我们需要实现
下面我们实现了一个捕获OOM的
Android应用开发中不可避免地会发生崩溃,特别是在用户使用过程中,一些特定场景的偶然概率的crash会通常让开发者抓狂。幸运的是Android提供了处理这类问题的方法,当App Crash时,我们可以记录下Crash的原因或者是一些设备信息,并上传到服务器供开发者分析,以便开发者迅速定位问题原因。
实现这个功能我们需要实现
Thread.UncaughtExceptionHandler这个接口。里面只有一个方法
uncaughtException。当我们注册一个
UncaughtExceptionHandler之后,当我们的程序crash时就会回调
uncaughtException方法,在
uncaughtException方法中就可以获取到异常信息,我们可以选择把异常信息存储到SD卡中,然后在合适的时机上传到服务器。或者我们还可以在crash发生时,弹出一个对话框告诉用户程序crash了,然后再退出,这样会比闪退温和一些。
下面我们实现了一个捕获OOM的
UncaughtExceptionHandler的类,发生OOM时可以抓取一些日志,然后可以通过MAT分析生成的hprof文件来定位问题。下面来看一下这个类的用法:
CrashHandler类
public class OomCrashHandler implements Thread.UncaughtExceptionHandler { final String TAG = "OomCrashHandler"; private Thread.UncaughtExceptionHandler mDefaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); private String mPackageName; private int mPid; private StringBuilder mFilename = new StringBuilder(120); private final String OOM_TARGET_FOLDER = "/crash_log/"; private static final long ONE_DAY_TIME_IN_MILLISECONDS = 24*60*60*1000; private boolean mDumpingHprof; OomCrashHandler(String pkgName, int pid) { this.mPackageName = pkgName; this.mPid = pid; this.mDumpingHprof = false; } public void uncaughtException(Thread thread, Throwable throwable) { if(throwable instanceof OutOfMemoryError && !mDumpingHprof) { Log.w(TAG, "OomCrashHandler capture a oom exception !!!"); if(!dumpHprofData()) { Log.e(TAG, "Aborting ..."); }else{ //uploadExceptionToServer(); // TODO } } if(mDefaultUncaughtExceptionHandler != null){ mDefaultUncaughtExceptionHandler.uncaughtException(thread, throwable); } else { Process.killProcess(Process.myPid()); } } //获取目录大小,单位 M private double getDirSize(File file) { if(!file.exists()) { Log.w(TAG, file.toString() + " may not exists !"); return 0.0D; } else if(!file.isDirectory()) { double size = (double)file.length() / 1024.0D / 1024.0D; return size; } else { File[] files = file.listFiles(); double size = 0.0D; int length = files.length; for(int i = 0; i < length; ++i) { File f = files[i]; size += this.getDirSize(f); } return size; } } //当目录大于1G时且创建时间大于1天时删除旧文件 private void deleteOldFiles(File file) { if(getDirSize(file) >= 1024.0D) { Log.w(TAG, "begin to delete old files !"); long currentTimeMillis = System.currentTimeMillis(); File[] listFiles = file.listFiles(); for(int i = 0; i < listFiles.length; ++i) { if(listFiles[i].isFile()) { if(currentTimeMillis - listFiles[i].lastModified() > ONE_DAY_TIME_IN_MILLISECONDS) { listFiles[i].delete(); } } else if(listFiles[i].isDirectory()) { deleteOldFiles(listFiles[i]); } } } } private boolean getDumpDestinationOfHeap() { mFilename.delete(0, mFilename.length()); mFilename.append(Environment.getExternalStorageDirectory()); mFilename.append(OOM_TARGET_FOLDER); File target = new File(mFilename.toString()); if (!target.exists()) { if (!target.mkdirs()) { Log.e(TAG, "Creating target hprof directory: \"" + mFilename.toString() + "\" was failed!"); return false; } } deleteOldFiles(target); mFilename.append(mPackageName); mFilename.append("_PID:" + mPid); String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(System.currentTimeMillis())); mFilename.append("_TIME:"+time); mFilename.append(".hprof"); target = new File(mFilename.toString().trim()); try { target.createNewFile(); } catch (IOException e) { Log.e(TAG, "Creating target hprof file: \"" + mFilename.toString() + "\" was failed! Reason:" + e); return false; } return true; } private boolean dumpHprofData() { if (!getDumpDestinationOfHeap()) { return false; } Log.w(TAG, "Begin to dump hprof to " + mFilename.toString()); long beginDumpTime = SystemClock.uptimeMillis(); try { mDumpingHprof = true; Debug.dumpHprofData(mFilename.toString()); } catch (IOException e) { Log.e(TAG, "Dump hprof to " + mFilename.toString() + " failed ! " + e); return false; } long endDumpTime = (SystemClock.uptimeMillis() - beginDumpTime) / 1000; Log.w(TAG, "Dump succeed!, Took " + endDumpTime + "s"); return true; } public static void registerExceptionHandler(Context context) { OomCrashHandler handler = new OomCrashHandler(context.getPackageName(), android.os.Process.myPid()); Thread.setDefaultUncaughtExceptionHandler(handler); } }
调用
private void createOOM(){ OomCrashHandler.registerExceptionHandler(this); ArrayList<Bitmap> arrayList = new ArrayList<>(); for(int i = 0; i<100000; i++){ arrayList.add(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)); } }
相关文章推荐
- 使用CrashHandler来获取(收集)应用的crash信息
- 使用CrashHandler来获取应用的crash信息
- Android 使用 CrashHandler 来获取应用的 crash 信息
- 使用CrashHandler来获取应用的Crash信息
- 从框架到完整项目搭建,实战项目《约个球》(2)-框架搭建之使用CrashHandler来获取应用的Crash信息
- 使用CrashHandler来获取应用的crash信息
- Android 使用CrashHandler获取应用的crash信息
- Android使用CrashHandler来获取应用的crash信息的方法
- Android 使用CrashHandler获取应用的crash信息
- 使用python编写脚本获取手机当前应用apk的信息
- 使用python编写脚本获取手机当前应用的信息
- Win10 9926如何使用全新的地图应用以便获取更多信息
- Android群英传——第九章使用PackageManager获取应用包信息
- 通过CrashHandler获取应用的crash信息
- android 程序崩溃信息的收集【使用CrashHandler来收集应用的crash信息】
- 使用python编写脚本获取手机当前应用apk的信息
- UsageStatsManager怎样使用问题详解和获取应用使用信息
- 使用PackageManager来获取应用信息
- Python 如何获取Android应用内存使用和CPU占用信息,并且时时展示曲线图形。
- javascript 的高级应用 获取浏览器的信息:使用navigator对象