使用CrashHandler来获取应用的oom crash信息

本文介绍了如何使用CrashHandler来捕获和处理Android应用的OOM崩溃,包括记录异常信息、存储日志和分析hprof文件,以帮助开发者定位问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我的博客原文地址
GitHub源码地址
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));
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寒江蓑笠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值