You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JNI SIGSEGV崩溃Java捕获失败,求解决方案及排查思路

在Android独立进程Service中捕获Native层SIGSEGV崩溃的问题

我已将触发SIGSEGV的Native代码移至标记为android:process=":ffmpeg"的独立进程Service中,避免主进程崩溃,但尝试三种方案均无法在Java层捕获崩溃并上报:

  • 在C信号处理器中调用throwFfmpegHelperException(a, "throwFfmpegHelperException message")抛出Java异常
  • 在C信号处理器中调用Java静态方法nativeCrashed()
  • 为执行Native调用的Java线程设置UncaughtExceptionHandler

目前只能通过服务回复超时判断崩溃,需要明确失败原因及替代方案。


方案失败原因分析

1. 信号处理器中直接抛出Java异常无效

JNI环境(JNIEnv*)是线程绑定的,信号处理器运行在信号处理上下文,而非原JNI调用线程,直接使用保存的JNIEnv*会导致未定义行为。此外,信号处理函数中不能安全调用大部分JNI函数(如ThrowNew),因为这些函数可能涉及线程状态、锁操作,在信号上下文执行会引发死锁或崩溃。

2. 信号处理器中调用Java方法无效

同理,信号上下文无法安全执行JNI的方法调用(CallStaticVoidMethod),且保存的jclass未做全局引用,可能已被GC回收,导致调用失败。

3. UncaughtExceptionHandler无法捕获Native崩溃

UncaughtExceptionHandler只能处理Java层未捕获的异常,Native层的SIGSEGV属于操作系统信号,会直接终止进程,不会转化为Java异常,因此该处理器无法触发。


可行解决方案

方案1:信号处理器中通过管道安全通知Java层

在信号处理器中,不要直接调用JNI方法,而是通过**管道(Pipe)**向Java层发送信号,Java层通过FileDescriptor监听管道读取事件,收到信号后触发崩溃上报逻辑。这种方式利用异步信号安全的write函数,避免JNI调用的风险。

方案2:Native崩溃栈捕获+进程退出感知

利用Android的libunwind或breakpad捕获Native崩溃栈并写入文件,Java层通过ServiceConnection的onServiceDisconnected回调感知独立进程退出,读取崩溃文件完成上报。

方案3:优化独立进程生命周期监听

结合onServiceDisconnected回调与超时逻辑:当服务断开且未收到预期回复时,判定为Native崩溃导致进程终止,触发上报。


代码修正示例(管道通知方案)

C层代码修改

#include <unistd.h>
#include <signal.h>

static int crash_pipe[2];

void Segfault_Handler(int signo) {
    // 向管道写入信号标识,通知Java层
    char msg = 'c';
    write(crash_pipe[1], &msg, 1);
    // 恢复默认信号处理,让进程正常退出
    signal(signo, SIG_DFL);
    raise(signo);
}

jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
    JNIEnv *env = NULL;
    jclass cls;

    if ((*jvm)->AttachCurrentThread(jvm, &env, NULL) != JNI_OK) {
        return JNI_ERR;
    }
    cls = (*env)->FindClass(env, "com/schokoladenbrown/Smooth");
    (*env)->RegisterNatives(env, cls, g_native_methods, sizeof(g_native_methods)/sizeof(g_native_methods[0]));
    nativeCrashed = (*env)->GetStaticMethodID(env, cls, "nativeCrashed", "()V");
    
    // 初始化管道
    pipe(crash_pipe);
    // 设置SIGSEGV信号处理器
    signal(SIGSEGV, Segfault_Handler);
    
    return JNI_VERSION_1_6;
}

// 暴露方法给Java层获取管道读端文件描述符
JNIEXPORT jint JNICALL
Java_com_schokoladenbrown_Smooth_getCrashPipeReadFd(JNIEnv *env, jclass clazz) {
    return crash_pipe[0];
}

Java层Service监听管道

public class FfmpegBoundService extends Service {
    static final int MSG_SAY_HELLO = 1;
    static final int MSG_CRASH_REPORT = 11;
    
    Messenger clientMessenger;
    private FileDescriptor crashPipeFd;
    private InputStream crashInputStream;
    private Thread crashMonitorThread;

    static class IncomingHandler extends Handler {
        Bitmap tb;
        @Override
        public void handleMessage(Message msg) {
            // 原有消息处理逻辑...
        }
    }

    Messenger mMessenger;

    @Override
    public void onCreate() {
        super.onCreate();
        // 初始化崩溃监听管道
        initCrashMonitor();
    }

    private void initCrashMonitor() {
        int fd = Smooth.getCrashPipeReadFd();
        crashPipeFd = new FileDescriptor();
        crashPipeFd.setInt$(fd);
        crashInputStream = new FileInputStream(crashPipeFd);
        
        crashMonitorThread = new Thread(() -> {
            byte[] buffer = new byte[1];
            try {
                // 阻塞等待管道数据
                if (crashInputStream.read(buffer) != -1) {
                    reportCrash("Native SIGSEGV crash detected");
                    stopSelf();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        crashMonitorThread.start();
    }

    private void reportCrash(String msg) {
        Log.e("FfmpegService", msg);
        // 通知主进程崩溃事件
        if (clientMessenger != null) {
            try {
                Message crashMsg = Message.obtain(null, MSG_CRASH_REPORT, msg);
                clientMessenger.send(crashMsg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        mMessenger = new Messenger(new IncomingHandler());
        return mMessenger.getBinder();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        try {
            crashInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

内容的提问来源于stack exchange,提问作者usilo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 20:31:04