Android全局异常捕获,解决日志打印三次的BUG

本文介绍了一种处理Android应用程序全局异常的方法,并解决了异常日志重复打印三次的问题。通过自定义异常处理器CrashHandler,确保日志仅记录一次,并在处理后安全退出程序。

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

最近写的项目需要自己写全局的异常捕捉,所以百度了很多解决方案,发现出现各种问题,好不容易找到一些比较靠谱的方案,但是却发现出现了一个让我无语的问题——日志打印输出三次。于是又开始去寻找答案,发现并没有相关的解决。好嘛,看来还是要自己搞了。

发生该现象的基本原因就是:Application没有完全退出,导致同一个异常多次执行,于是出现了同一个异常打印多次的问题


【解决方案】

  • 处理全局异常的Handler:CrashHandler.java,代码如下:
public class CrashHandler implements UncaughtExceptionHandler {
    private static CrashHandler instance;
    private UncaughtExceptionHandler mDefaultHandler;

    public void init(Context ctx) {
        Thread.setDefaultUncaughtExceptionHandler(this);
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();// 获取系统默认的UncaughtException处理器

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

    /**
     * 核心方法,当程序crash 会回调此方法, Throwable中存放这错误日志
     */
    @Override
    public synchronized void uncaughtException(Thread arg0, Throwable arg1) {
        if (mDefaultHandler != null && !handleException(arg1)) {
            // 如果自定义的没有处理则让系统默认的异常处理器来处理
            mDefaultHandler.uncaughtException(arg0, arg1);
        } else {
            // 退出程序
            try {
                Thread.sleep(3000);// 如果处理了,让程序继续运行3秒再退出,保证文件保存并上传到服务器
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Myapplication.getApplication().closeApplication();//完全退出Application
//          如果使用如下方法退出程序,将会发生日志打印三次的BUG
//          android.os.Process.killProcess(android.os.Process.myPid());
//          System.exit(1);
        }
    }

    private boolean handleException(Throwable arg1) {
        String logPath;
        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) {
            logPath = Environment.getExternalStorageDirectory()
                    .getAbsolutePath()
                    + File.separator
                    + File.separator
                    + "log";
            File file = new File(logPath);
            if (!file.exists()) {
                file.mkdirs();
            }
            try {
                FileWriter fw = new FileWriter(logPath + File.separator
                        + CommonUtil.getStrTime() + ".log", false);
                StringBuffer sb = new StringBuffer();
                sb.append("/*************************************************/\n");
                sb.append(new Date() + "\n");
                // 错误信息
                // 这里还可以加上当前的系统版本,机型型号 等等信息
                StackTraceElement[] stackTrace = arg1.getStackTrace();
                sb.append("错误信息:" + arg1.getMessage() + "\n");
                for (int i = 0; i < stackTrace.length; i++) {
                    String info = "文件:" + stackTrace[i].getFileName() + "   类:"
                            + stackTrace[i].getClassName() + "   方法:"
                            + stackTrace[i].getMethodName() + "   行号:"
                            + stackTrace[i].getLineNumber() + "\n";
                    sb.append(info);
                }
                sb.append("/*************************************************/\n");
                fw.write(sb.toString());
                fw.close();
                // 上传错误信息到服务器
                uploadToServer(sb);
            } catch (IOException e) {
                Log.e("crash handler", "load file failed...", e.getCause());
            }
        }
        return true;
    }

    private void uploadToServer(StringBuffer sb) {
        //执行上传操作,错误信息为:sb.toString();
    }

}
  • Handler的初始化以及MyApplication的编写:MyApplication.java
public class Myapplication extends Application {
    private List<Activity> activitys = new LinkedList<Activity>();
    private List<Service> services = new LinkedList<Service>();
    private static int mTid;
    private static Myapplication mApplication;
    private CrashHandler crashHandler;

    @Override
    public void onCreate() {
        super.onCreate();
        mApplication = this;
        mTid = android.os.Process.myTid();
        crashHandler = CrashHandler.getInstance();
        crashHandler.init(getApplicationContext());
    }

    /**
     * 
     * TODO 获取活动队列
     */
    public List<Activity> getActivitys() {
        return activitys;
    }

    /**
     * 
     * TODO 添加Activity
     */
    public void addActivity(Activity activity) {
        activitys.add(activity);
    }

    /**
     * 
     * TODO 移除Activity
     */
    public void removeActivity(Activity activity) {
        activitys.remove(activity);
    }

    /**
     * 
     * TODO 添加服务
     */
    public void addService(Service service) {
        services.add(service);
    }

    /**
     * 
     * TODO 移除Service
     */
    public void removeService(Service service) {
        services.remove(service);
    }

    /**
     * 
     * TODO 关闭程序
     */
    public void closeApplication() {
        closeActivitys();
        closeServices();
        android.os.Process.killProcess(android.os.Process.myPid());
    }

    /**
     * 
     * TODO 关闭所有的Activity
     */
    public void closeActivitys() {
        ListIterator<Activity> iterator = activitys.listIterator();
        while (iterator.hasNext()) {
            Activity activity = iterator.next();
            if (activity != null) {
                activity.finish();
            }
        }
    }

    /**
     * 
     * TODO 关闭所有的Service
     */
    private void closeServices() {
        ListIterator<Service> iterator = services.listIterator();
        while (iterator.hasNext()) {
            Service service = iterator.next();
            if (service != null) {
                stopService(new Intent(this, service.getClass()));
            }
        }
    }


    /**
     * 
     * TODO 获取Application
     */
    public static Myapplication getApplication() {
        return mApplication;
    }

    /**
     * 
     * TODO 获取主线程PID
     */
    public static int getmTid() {
        return mTid;
    }

}
  • BaseActivity的编写:BaseActivity.java
public class BaseActivity extends Activity {
    protected BaseActivity mActivity;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);// 保存生命周期的完整性
        ((Myapplication) getApplication()).addActivity(this);
        mActivity = this;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ((Myapplication) getApplication()).removeActivity(this);
    }
}

总结

以上代码仅供参考,可能有更好的解决方法,欢迎大家给我留言。本人Android菜鸟一枚,并非大牛,可能部分代码不尽如人意,希望各位大大可以予以指出,我会虚心接受并积极改正,谢谢大家!

<think>好的,用户给出了一段日志,里面多次出现SIGABRT信号导致的崩溃。我需要先分析SIGABRT的原因,然后根据可能的常见问题来推测问题所在,并提供解决方法。 首先,SIGABRT通常是程序主动调用了abort()函数引发的,常见于检测到严重错误,比如内存损坏、断言失败、堆栈溢出等。日志中多次不同进程的崩溃,可能意味着问题具有普遍性,而不是某个特定进程的问题。例如,可能代码中有共同的逻辑错误,或者资源竞争、多线程问题。 接下来,用户需要逐步排查。第一步是查看崩溃时的堆栈跟踪,比如使用adb logcat或Android Studio的调试工具,找到abort的具体位置。如果没有堆栈信息,可能需要配置NDK的addr2line或使用breakpad来符号化堆栈。 然后,考虑可能的原因:内存问题如越界访问、使用已释放内存;断言失败,比如检查条件不满足;资源泄露如文件描述符或内存泄露导致系统杀死进程;多线程问题如死锁或不正确同步;第三方库或系统API的误用;JNI层的错误,比如本地代码未处理异常。 解决方法方面,建议用户启用更详细的日志,使用AddressSanitizer或Valgrind检测内存错误,检查代码中的断言和错误处理逻辑,检查资源管理,分析多线程代码,以及审查JNI代码。此外,还可以尝试复现问题并逐步调试,或者查看系统日志中是否有相关错误信息。 需要确保回答结构清晰,分点列出可能原因和解决方法,帮助用户逐步排查问题。同时,注意使用中文,并正确应用LaTeX格式,但根据用户提供的日志内容,可能不需要数学公式,所以主要使用自然语言解释。</think>这段日志中的`SIGABRT`崩溃问题分析及解决方法如下: --- ### **一、问题本质** `SIGABRT`(Signal 6)是进程主动终止自身的信号,通常由以下原因触发: 1. **内存操作错误**:如堆栈溢出、双重释放(double-free)、非法指针访问 2. **断言失败**:代码中`assert()`检查到非法状态 3. **未捕获的C++异常**:如`std::bad_alloc`(内存分配失败) 4. **Android系统限制**:如文件描述符泄漏超过阈值(通过`adb shell lsof -p PID`可验证) 5. **JNI层错误**:本地代码(Native Code)未正确处理异常 --- ### **二、关键日志特征分析** ```log # 特征示例 pid-3360 A signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr -------- ``` - `fault addr --------`表示无明确内存地址,说明崩溃点可能在逻辑检查层而非直接的内存访问 - 多进程连续崩溃(pid-3360→pid-3652)暗示存在 **公共组件问题** 或 **资源竞争** --- ### **三、排查步骤** #### 1. 获取完整堆栈轨迹 通过以下命令捕获Native崩溃堆栈: ```bash adb logcat -b crash | grep "pid-3360" ``` 或使用Android Studio的`Debug`视图捕获符号化后的堆栈 #### 2. 常见问题验证 | 问题类型 | 验证方法 | |------------------|--------------------------------------------------------------------------| | 内存泄漏 | 使用`Android Profiler`观察内存增长曲线 | | JNI错误 | 检查`JNIEnv`调用是否在正确的线程,或通过`CheckJNI`模式运行(`adb shell setprop debug.checkjni 1`) | | 文件描述符泄漏 | `adb shell ls -l /proc/<PID>/fd` | | 堆栈溢出 | 检查递归函数或大体积局部变量(如`char buf[1024*1024]`) | #### 3. 代码审查重点 - **断言语句**:检查`assert(condition)`中`condition`的合法性 - **资源释放**:成对操作(如`malloc/free`、`open/close`)是否遗漏 - **多线程同步**:共享数据是否通过锁(`pthread_mutex`)保护 - **第三方库兼容性**:观察崩溃是否仅在特定API版本或设备上复现 --- ### **四、解决方法** #### 1. 内存问题定位 ```bash # 使用AddressSanitizer(ASan)编译调试版本 ndkBuildArgs += "-fsanitize=address -fno-omit-frame-pointer" ``` ASan会检测内存越界、释放后使用(use-after-free)等问题 #### 2. 捕获JNI崩溃 在JNI入口添加全局异常处理: ```cpp jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env; if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) return -1; // 注册全局异常处理器 if (sigsetjmp(env->GetLongField(...), 1)) { __android_log_print(ANDROID_LOG_ERROR, "JNI", "Native crash caught!"); return JNI_ERR; } return JNI_VERSION_1_6; } ``` #### 3. 监控资源泄漏 ```bash # 监控文件描述符数量 adb shell watch -n 1 "ls -l /proc/`pidof your.package`/fd | wc -l" ``` --- ### **五、典型修复案例** - **案例1**:某图片加载库因未关闭`Bitmap`导致FD泄漏 → 修复后FD数量稳定在50以下 - **案例2**:JNI中未检查`FindClass()`返回值 → 添加`if (clazz == NULL) return;`后崩溃消失 建议优先通过`adb bugreport`导出完整系统日志进行深度分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值