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

是否需单独Native函数库加载含main函数的库?技术咨询

解答你的SDL Android库加载与main函数执行问题

首先明确说:不需要单独的native函数库来加载包含main函数的库,你当前把业务逻辑(含SDL_main)编译到main.so的思路是符合SDL for Android的标准实践的,但代码实现里的细节错误导致了应用无反应、日志看不到的问题。下面一步步拆解问题和解决方案:


1. 核心问题:你的nativeRunMain函数陷入了无限递归

你写的JNI函数代码里,直接硬编码调用了自身:

JNIEXPORT int JNICALL Java_org_libsdl_app_SDLActivity_nativeRunMain(jstring library, jstring function, jobject array) { 
    return Java_org_libsdl_app_SDLActivity_nativeRunMain("libmain.so", "SDL_main", NULL); 
}

这会导致函数无限调用自己,程序直接卡死,根本没机会执行真正的SDL_main入口。正确的做法应该是通过动态符号查找,加载main.so并调用其中的SDL_main:

#include <jni.h>
#include <dlfcn.h>
#include <android/log.h>

#define LOG_TAG "SDL"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

JNIEXPORT int JNICALL Java_org_libsdl_app_SDLActivity_nativeRunMain(JNIEnv* env, jobject thiz, jstring library, jstring function, jobject array) {
    // 1. 获取库路径的UTF-8字符串
    const char* libPath = (*env)->GetStringUTFChars(env, library, NULL);
    if (!libPath) {
        LOGE("Failed to get library path string");
        return -1;
    }

    // 2. 动态加载main.so
    void* libHandle = dlopen(libPath, RTLD_NOW);
    if (!libHandle) {
        LOGE("dlopen %s failed: %s", libPath, dlerror());
        (*env)->ReleaseStringUTFChars(env, library, libPath);
        return -1;
    }

    // 3. 获取SDL_main函数指针
    const char* funcName = (*env)->GetStringUTFChars(env, function, NULL);
    if (!funcName) {
        LOGE("Failed to get function name string");
        dlclose(libHandle);
        (*env)->ReleaseStringUTFChars(env, library, libPath);
        return -1;
    }

    typedef int (*SDLMainFunc)(int, char**);
    SDLMainFunc sdlMain = (SDLMainFunc)dlsym(libHandle, funcName);
    if (!sdlMain) {
        LOGE("dlsym %s failed: %s", funcName, dlerror());
        dlclose(libHandle);
        (*env)->ReleaseStringUTFChars(env, library, libPath);
        (*env)->ReleaseStringUTFChars(env, function, funcName);
        return -1;
    }

    // 4. 执行SDL_main(Android环境下通常不需要传参数,传0和NULL即可)
    int result = sdlMain(0, NULL);

    // 5. 释放资源
    dlclose(libHandle);
    (*env)->ReleaseStringUTFChars(env, library, libPath);
    (*env)->ReleaseStringUTFChars(env, function, funcName);

    return result;
}

2. 库加载顺序的正确性

你当前的getLibraries返回顺序:

protected String[] getLibraries() { 
    return new String[] { "SDL2", "hidapi", "native", "main" }; 
}

这个顺序是合理的——先加载main.so依赖的基础库(SDL2、hidapi等),再加载业务库main.so,只要编译时main.so正确链接了这些依赖库,就不会有问题。


3. 为什么看不到log_android的输出?

先解决上面的递归问题后,再按以下步骤排查日志:

  • SDL默认的Android日志标签是SDL,可以用adb命令过滤查看:
    adb logcat SDL:V *:S
    
  • 确保编译SDL时开启了日志功能:编译SDL时要定义SDL_LOGGING相关宏(官方SDL Android模板默认已开启)。
  • 检查main.so是否正确包含了SDL_main的实现,可以用nm工具查看符号:
    arm-linux-androideabi-nm -D libmain.so | grep SDL_main
    
    如果能输出对应的符号,说明SDL_main存在。

4. 包含main函数库的最佳实践

在SDL for Android的生态里,把业务逻辑(含SDL_main)编译到main.so,通过SDLActivity的JNI接口启动是标准做法,不需要额外的加载库。你可以参考官方的SDL Android模板,复用其已经验证过的nativeRunMain实现,避免自己踩坑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 07:29:27