Android Crash捕获及处理

不只是Crash捕获:用UncaughtExceptionHandler处理Android系统级异常DeadSystemException的完整思路 本文深入探讨了Android系统级异常DeadSystemException的全局捕获与优雅恢复策略。通过构建分层防御体系,包括UncaughtExceptionHandler基础捕获层和Binder调用代理增强层,开发者可以有效处理系统服务崩溃引发的连锁反应。文章还详细解析了主线程循环恢复技术和应用冷启动方案,帮助开发者构建健壮的异常处理框架,显著提升应用稳定性。 阅读详情

一、Android Crash说明
程序因未捕获的异常而突然终止,系统会调用UncaughtExceptionHandler接口来处理未被程序正常捕获的异常,该接口含有UncaughtExceptionHandler方法,UncaughtExceptionHandler方法回传了 Thread 和 Throwable 两个参数。

public class CrashHandler implements Thread.UncaughtExceptionHandler{

    @Override
    public void uncaughtException(Thread t, Throwable e) {

    }
}

只需要实现UncaughtExceptionHandler接口,并调用Thread.setDefaultUncaughtExceptionHandler()方法,即可以获取到未捕获的异常,并可以进行相应的crash上报

二、实现思路
首先收集产生崩溃的手机信息,因为Android的样机种类繁多,很可能某些特定机型下会产生莫名的bug,需要将手机的信息和崩溃信息手机并上传,以便于在后续版本中进行修复。同时也可以在Debug模式下将Crash日志写入到本地文件中,便于及时分析和处理。

三、实现方法

public class CrashHandler implements UncaughtExceptionHandler {
    /**
     * crash文件地址,后续可以扫描这个地址,将crash上报至服务器
     */
    public static final String CRASH_PATH = "/crash/log/";

    /**
     * mDefaultHandler
     */
    private UncaughtExceptionHandler mDefaultHandler;

    /**
     * instance
     */
    private static CrashHandler instance = new CrashHandler();

    /**
     * infos
     */
    private Map<String, String> infos = new HashMap<String, String>();

    private CrashHandler() {
    }

    public static CrashHandler getInstance() {
        if (instance == null) {
            instance = new CrashHandler();
        }
        return instance;
    }

    /**
     * 初始化
     *
     */
    public void init() {
        // 获取系统默认的UncaughtException处理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        // 设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 当UncaughtException发生时会转入该函数来处理
     */
    @Override
    public void uncaughtException(Thread thread, Throwable throwable) {
        if (!handleException(throwable)) {
            mDefaultHandler.uncaughtException(thread, throwable);
        }
    }

    /**
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
     *
     * @param exception
     * @return true:如果处理了该异常信息;否则返回false.
     */
    private boolean handleException(Throwable exception) {
        if (exception == null) {
            return false;
        }
        collectDeviceInfo();
        writeCrashInfoToFile(exception);
        return true;
    }

    /**
     * 相关的crash信息
     */
    public void collectDeviceInfo() {
        infos.put(CrashCollectionParameter.VERSION_NAME, BaseConfig.versionName);
        infos.put(CrashCollectionParameter.VERSION_CODE, String.valueOf(BaseConfig.versionCode));
        infos.put(CrashCollectionParameter.CRASH_TIME, new SimpleDateFormat().format(new Date()));
        infos.put(CrashCollectionParameter.DEVICE_ID, BaseConfig.deviceId);
        infos.put(CrashCollectionParameter.NET_TYPE, BaseConfig.getNetwork());
        infos.put(CrashCollectionParameter.APP_CHANNEL, BaseConfig.channel);
        infos.put(CrashCollectionParameter.SYSTEM_VERSION, BaseConfig.os);
        infos.put(CrashCollectionParameter.DEVICE_RESOLUTION, BaseConfig.resolution);
        infos.put(CrashCollectionParameter.APP_MODE, BaseConfig.appMode);

        // 获取Build.class中定义的基本信息
        Field[] fields = Build.class.getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                infos.put(field.getName(), field.get(null).toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @param ex 将崩溃写入文件系统
     */
    private void writeCrashInfoToFile(Throwable ex) {
        final StringBuffer sb = new StringBuffer();
        for (Map.Entry<String, String> entry : infos.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key + "=" + value + "\n");
        }

        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();
        sb.append(result);

        // 将crash信息写入SD卡的Log日志里面
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            String sdcardPath = Environment.getExternalStorageDirectory().getPath();
            final String filePath = sdcardPath + CRASH_PATH;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    writeLog(sb.toString(), filePath);
                }
            }).start();
        }
    }

    /**
     * @param log
     * @param name
     * @return 将crash信息写入文件,返回写入的文件名称,便于后续将crash上传至服务器
     */
    private void writeLog(String log, String name) {
        CharSequence timestamp = new Date().toString().replace(" ", "");
        String filename = name + timestamp + ".log";

        File file = new File(filename);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        try {
            file.createNewFile();
            FileWriter fw = new FileWriter(file, true);
            BufferedWriter bw = new BufferedWriter(fw);
            bw.write(log);
            bw.newLine();
            bw.close();
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Crash相关参数,可以根据需要收集更多参数

public final class CrashCollectionParameter {
    /**
     * 版本信息
     */
    public static final String VERSION_NAME = "versionName";

    /**
     * 版本号
     */
    public static final String VERSION_CODE = "versionCode";

    /**
     * Crash发生时间,应尽量使用服务器时间
     */
    public static final String CRASH_TIME = "crashTime";

    /**
     * 设备DeviceID
     */
    public static final String DEVICE_ID = "deviceID";

    /**
     * 网络类型
     */
    public static final String NET_TYPE = "netType";

    /**
     * 渠道类型
     */
    public static final String APP_CHANNEL = "appChannel";

    /**
     * 系统版本
     */
    public static final String SYSTEM_VERSION = "systemVersion";

    /**
     * 分辨率
     */
    public static final String DEVICE_RESOLUTION = "deviceResolution";

    /**
     * App模式,用于区分是Debug、Release等
     */
    public static final String APP_MODE = "appMode";

}

在APP启动时需要获取设备相关信息

public final class BaseConfig {
    public static int width;
    public static int height;
    public static String resolution;
    public static String versionName;
    public static int versionCode;
    public static String channel;
    public static String appMode;
    public static String deviceId = "000000000000000";
    public static String networkType;
    public static String networkSubtype;
    public static String os;

    public static void init(Context context) {
        try {
            PackageInfo pi = context.getPackageManager().getPackageInfo(context.getPackageName(),
                    PackageManager.GET_CONFIGURATIONS);
            versionName = pi.versionName;
            versionCode = pi.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
        }

        TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        deviceId = tm.getDeviceId();

        initDisplay(context);
        initNetwork(context);

        try {
            ApplicationInfo applicationInfo = context.getPackageManager()
                    .getApplicationInfo(context.getPackageName(),
                            PackageManager.GET_META_DATA);
            channel = applicationInfo.metaData.getString("channel");
            appMode = applicationInfo.metaData.getString("appMode");
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }

        os = Build.VERSION.RELEASE;
    }

    public static void initNetwork(Context context) {
        NetworkInfo ni = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
        networkType = ni == null ? "" : ni.getTypeName();
        networkSubtype = ni == null ? "" : ni.getSubtypeName();
    }

    public static String getNetwork() {
        String net;
        if ("MOBILE".equals(networkType)) {
            net = TextUtils.isEmpty(networkSubtype) ? networkType : networkSubtype;
        } else {
            net = networkType;
        }
        return net;
    }

    public static void initDisplay(Context context) {
        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        width = metrics.widthPixels;
        height = metrics.heightPixels;
        resolution = new StringBuilder().append(width).append('*').append(height).toString();
    }
}

同时需要在Application启动时初始化BaseConfig和CrashHandler

public class CrashTestApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        BaseConfig.init(this);
        CrashHandler crashHandler = CrashHandler.getInstance();
        crashHandler.init();
    }
}

四、后续操作
现在crash都已经以日志的形式保存在了SDCrad中,后续可以在用户连接WiFi后,遍历crash日志目录,将crash日志上传至服务器,上传成功后还可以根据需要将手机上的crash日志删除

Android Native Crash 捕获之 BreakPad Breakpad 是一个库和工具套件,具有一定的跨平台能力,google 浏览器,Android 等都有利用其进行 Native 捕获,他可以把已发布的脱表应用程序 Native 崩溃记录在一个 dump 文件中,然后我们可以把这个文件捞回服务器,并从这些 minidump 文件里的 C 和 C++ 堆栈分析崩溃问题原因。他也能根据请求把没有崩溃的程序 Native 堆栈写入 minidump,类似 Java 层主动打印调用栈能力。其项目地址为。编译适合自己平台的库。 阅读详情

相关推荐

Android稳定性系列7 Java crash处理流程

一 概述 App crash(全称Application crash), 对于Crash可分为native crash和framework crash(包含app crash在内),对于crash相信很多app开发者都会遇到,那么上层什么时候会出现crash呢,系统又是如何处理crash的呢。例如,在app大家经常使用try…catch语句,那么如果没有有效catch exception,就是导致应用crash,发生没有catch exception,系统便会来进行捕获,并进入crash流程。如果你是从事A

liuwg1226的专栏 1184

Android 平台 Native 代码的崩溃捕获机制及实现

在前一步,我们获取了奔溃时的pc值和各个寄存器的内容,通过SP和FP所限定的stack frame,就可以得到母函数的SP和FP,从而得到母函数的stack frame(PC,LR,SP,FP会在函数调用的第一时间压栈),以此追溯,即可得到所有函数的调用顺序。在我的实验中,在子进程或者信号处理函数中,经常无法回调给java层。下面是Google Breakpad的流程图,在新的进程中DoDump,使用ptrace解析crash进程的堆栈,同时信号处理程序等待子进程完成任务后,再调用旧的信号处理函数。

idaretobe的专栏 2863

Android crash 流程详解(一):JE

App crash(全称为 Application crash),又分 java crash 和 native crash,又称 java layer exception(JE) 和 native layer exception(NE对于 crash 在开发过程中或多或少都会遇到,本文将整理总结 crash 原理,剖析系统是如何捕捉、处理这些 crash。因为篇幅较长,所以会分JE 和 NE 两部分各自剖析。

私房菜 3123

Android 性能优化系列:崩溃原因及捕获

java crash 由 JVM 触发处理,最终走到 /data/system/dropbox 目录用文件保存native crash 由管道通信建立 socket 接收通知,最终走到 /data/system/dropbox 目录用文件保存anr 由多种情况(事件、前后台服务)触发器处理,最终走到 /data/system/dropbox 目录用文件保存所有的 crash 处理在 Android 系统内部都会将对应的数据收集到 /data/system/dropbox 目录下。

Vincent的博客 3139

Java、Android的异常处理原理&Android Crash捕获、分发及处理原理+实战详解

本文将介绍Java异常及异常处理,以及Android的异常处理原理。 Android中异常(Crash)处理和捕获。 Android系统中Crash的处理、分发逻辑。 Crash优化建议。

卜大爷的博客 2万+

Android应用Crash异常处理机制详解

Crash在Android应用中指的是应用在运行时发生错误而突然终止的情况。这通常是由内存溢出、空指针异常、线程中断等问题引起的。Crash不仅会打断用户的操作流程,影响用户体验,还可能导致数据丢失和业务损失。在Java中,接口允许我们定义当线程因未被捕获的异常而即将终止时的处理逻辑。当一个线程抛出了一个未被捕获的异常,并且该线程没有自己的异常处理器时,JVM会调用该线程的对象的方法。如果没有为线程组定义任何处理逻辑,那么默认的行为是打印堆栈跟踪信息到System.err,并退出线程。

weixin_35751412的博客 2507

android自定义异常捕获,GitHub - apkdemo/AndroidCrashHelper: Android Crash Helper。安卓APP自定义异常处理器,当APP触发未捕获的异常时...

AndroidCrashHelperAndroid Crash Helper。安卓APP自定义异常处理器,Release状态下意外奔溃提示更加友好(代替系统默认的那个讨厌的对话框),不影响Debug模式下LogCat的打印。************************************************************************** ...

weixin_34246971的博客 272

AndroidCrashHandler:Android崩溃处理流程

我们写程序的时候都希望能写出一个没有任何Bug的程序,期望在任何情况下都不会发生程序崩溃。但没有一个程序员能保证自己写的程序绝对不会出现异常崩溃。特别是当你用户数达到一定数量级后,你也更容易发现应用不同情况下的崩溃。   对于还没发布的应用程序,我们可以通过测试、分析Log的方法来收集崩溃信息。但对已经发布的程序,我们不可能让用户去查看崩溃信息然后再反馈给开发者。所以,设计一个对于小白用户都可以轻松实现反馈的应用就显得很重要了。我这里结合我自己写的一个Demo,来分析从崩溃开始到崩溃信息反馈到我们服务器,我们程序都需要做什么。 当我们的程序因未捕获的异常而突然终止时,系统会调用处理程序的接口UncaughtExceptionHandler。如果我们想处理未被程序正常捕获的异常,只需实现这个接口里的uncaughtException方法,uncaughtException方法回传了Threa

android 自定义异常,AndroidCrashHelper: Android Crash Helper。安卓APP自定义异常处理器,当APP触发未捕获的异常时,使用该处理器自定义有好的奔溃提示页...

AndroidCrashHelperAndroid Crash Helper。安卓APP自定义异常处理器,Release状态下意外奔溃提示更加友好(代替系统默认的那个讨厌的对话框),不影响Debug模式下LogCat的打印。************************************************************************** ...

weixin_34191685的博客 265

Android崩溃异常捕获方法

主要介绍了Android崩溃异常捕获方法的相关资料,需要的朋友可以参考下

思博伦Spirent TestCenter创建单播流uni-stream

Traffic Generator列表中会出现刚才创建的uni-stream,创建完成。通常测试情况下,主要对Ethenet II和IPv4 Header进行配置。(通常情况下,如果流配置比较规范,选择任意端口接收,也没问题。编辑uni-stream基本信息。配置本条流由STC哪个端口接收。

辗转反测的博客 1683

android用对话框捕捉异常,AndroidCrashHelper: Android Crash Helper。安卓APP自定义异常处理器,当APP触发未捕获的异常时,使用该处理器自定义有好的奔溃提...

AndroidCrashHelperAndroid Crash Helper。安卓APP自定义异常处理器,Release状态下意外奔溃提示更加友好(代替系统默认的那个讨厌的对话框),不影响Debug模式下LogCat的打印。************************************************************************** ...

weixin_30979383的博客 141

基于新张量核范数的TRPCA:多维数据低秩稀疏分解的理论与实践

本文深入探讨了基于新张量核范数的TRPCA(张量鲁棒主成分分析)技术。该技术通过t-product运算和新定义的张量核范数,能有效从多维数据(如视频、彩色图像)中分离低秩背景与稀疏前景/噪声。文章结合理论、核心算法(ADMM、t-SVT)及图像恢复、视频背景建模等实战案例,展示了其在处理复杂多维数据分解任务中的强大优势。

weixin_29173777的博客 269

android系统界面信息异常,AndroidCrashHelper: Android Crash Helper。安卓APP自定义异常处理器,当APP触发未捕获的异常时,使用该处理器自定义有好的奔溃提...

AndroidCrashHelperAndroid Crash Helper。安卓APP自定义异常处理器,Release状态下意外奔溃提示更加友好(代替系统默认的那个讨厌的对话框),不影响Debug模式下LogCat的打印。************************************************************************** ...

weixin_42513028的博客 242

基于STM32F407VET6的智能巡检小车灰度循迹系统优化设计

本文详细介绍了基于STM32F407VET6主控芯片的智能巡检小车灰度循迹系统优化设计。通过将传感器输出从数字量升级为模拟量以获取更丰富的路面信息,并采用PD控制算法结合速度管理策略,有效提升了小车在复杂弯道上的循迹稳定性与适应性。文章分享了硬件电路抗干扰设计、软件滤波及实战调优经验,为嵌入式开发者提供了从“能跑”到“跑得稳”的系统性解决方案。

weixin_28273593的博客 443

拆解大疆Osmo Pocket:聊聊微型云台里那几根比头发丝还细的线,以及它为啥一开机就‘摇头晃脑’

本文深入拆解大疆Osmo Pocket微型云台的精密硬件结构,揭秘其开机自检时‘摇头晃脑’的奥秘。通过分析无刷电机、霍尔传感器和46AWG同轴线等核心组件,展现了大疆在微型云台设计中的工程智慧与创新技术,特别是FOC控制和陀螺仪的应用,实现了0.1°级的姿态控制精度。

weixin_27869497的博客 670

Android 未捕获异常crash崩溃日志的截取

Android应用不可避免地会发生crash,也成之为崩溃。无论你的程序写得多么的完美,总是无法完全避免crash的发生,可能是由于Android系统底层的bug,也可能是由于不充分的机型适配或者糟糕的网络情况,当crash发生时,系统会kill掉正在执行的程序,现象是闪退或者提示用户程序已经停止运行。这是对用户来说很不友好,也是开发者不远看到的。 /** * function: 截获

Denluoyia 1766

【Unity】Unity性能分析解决方案——UPR

由Unity中国团队提供的一套性能优化解决方案,其是一系列性能工具的集合,并可将收集的数据进行可视化。

Davidorzs的博客 1008

海神平台Crash监控SDK(Android)开发经验总结

海神平台是我们自主研发的一个移动端质量监控平台,从去年7月份开始至今,已陆续上线了Crash监控、ANR监控、网络监控、自定义错误等功能,目前已接入了公司内近10款APP(不区分Android和iOS平台)。本文将主要分享Android端在开发Crash监控SDK过程中的一些实践和经验。希望大家能有所收获。 一、Java层异常捕获 系统提供了一个钩子:Thread.setDefaultUncau...

chwnpp2的专栏 1810

103、模拟增益vs数字增益:信噪比、动态范围与噪声放大的权衡艺术及调试陷阱

摘要 本文深入探讨了模拟增益与数字增益在图像处理中的差异及其对信噪比、动态范围和噪声放大的影响。作者通过实际案例揭示了数字增益可能导致的噪点放大问题,分析了模拟增益在物理层面对信号和噪声的放大机制,以及数字增益对量化噪声的影响。文章详细比较了两种增益方式下信噪比的变化曲线,指出数字增益会平方放大量化噪声的致命缺陷。此外,作者总结了常见的调试陷阱,并提供了针对不同光照场景的增益分配策略建议。最后强调调试时应优先考虑模拟增益,严格控制数字增益幅度,并配合降噪算法优化以获得最佳图像质量。

zzfmlxs的博客 50

YOLOv8多传感器融合技术——摄像头阵列融合技术详解

本文详细介绍了基于YOLOv8的摄像头阵列融合技术,该技术通过多摄像头协同工作实现360度无死角环境感知。文章阐述了系统原理,包括全方位视野覆盖、冗余设计和深度感知等优势,并提供了完整的Python实现代码。关键技术涵盖多摄像头检测、立体匹配、3D投影和目标融合等模块,支持时间域融合提升稳定性。该系统可显著提高自动驾驶的环境感知能力,减少检测盲区,增强系统可靠性,是L4-L5级自动驾驶的关键技术之一。文章还展示了城市驾驶场景的实际应用,验证了该技术对复杂交通环境的适应能力。

u010117029的博客 213

Android 平台的Crash崩溃捕获-全

上层-java/kotlin: Android应用层java/kotlin的crash捕获相对容易。直接实现Thread.UncaughtExceptionHandler即可处理收集。Thread.UncaughtExceptionHandler:当某一线程因未捕获的异常而即将终止时,Java 虚拟机将使用 Thread.getUncaughtExceptionHandler() 查询该线程以获得其 UncaughtExceptionHandler 的线程,并调用处理程序的 uncaughtExcepti

Jason_Lee155的博客 5172

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

Android Crash

*** 当给定线程由于给定的未捕获异常而终止时调用此方法。* Java 虚拟机将忽略此方法引发的任何异常。* @param t 导致异常的线程* @param e 未捕获的异常*/

望佑 1449

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Framework篇 - framework 层的 crash 处理流程

本文源代码基于 Android 7.0。 App crash (全称 Application crash),对于 crash 可分为 native crash 和 framework crash (包含 app crash 在内),对于 crash 相信很多 app 开发者都会遇到,那么上层什么时候会出现 crash 呢,系统又是如何处理 crash 的呢? 例如,在 app 中大家经...

u014294681的博客 1728

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Android未捕获异常机制

一.Framework层的未捕获异常 二.Framework层未捕获异常避免弹窗方案 三.Native层的未捕获异常机制 四.Native层收集crash原理 五.Native层未捕获异常避免弹窗方案 一.Framework层的未捕获异常: 先说几个结论: ①只要把异常传给了系统,进程和进程组就会被干掉,不管哪个线程出现异常; ②如果异常不传给系统,主线程出现未捕获异常,进程也会死亡,但子线程不会...

aa642531的博客 858
上一篇: Java对象克隆方法(浅克隆、深克隆)
下一篇: android:versionCode和android:versionName
LearnHowToFly
博客等级 码龄11年 0粉丝 11原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值