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

Android平台调用opendir()返回目录不存在的问题求助

我之前在移植C++游戏到Android时也踩过这个坑,你遇到的问题核心原因其实很好理解:Android的assets目录并不是文件系统中的真实路径——Windows上你的资源是直接放在磁盘目录里的,所以opendir这类标准C库文件API能正常访问,但Android的assets是打包在APK内部的资源容器,没有对应的文件系统路径,用普通文件API自然会返回ENOENT(目录不存在)的错误。

下面给你两种可行的解决方案,你可以根据自己的代码架构选择:

方案1:用Android NDK的AssetManager API遍历assets目录

这是最推荐的原生适配方式,不需要修改太多游戏逻辑,直接利用Android提供的资源访问接口:

  1. 先获取AssetManager实例:
    如果你的JNI代码是从Java层调用的,可以直接把AssetManager作为参数传入;如果用的是NativeActivity框架,也可以通过ANativeActivity的assetManager成员直接获取。

  2. 用AAssetDir相关函数遍历目录:
    示例代码如下:

    #include <android/asset_manager.h>
    #include <android/asset_manager_jni.h>
    #include <iostream>
    
    void traverseJasonAssets(AAssetManager* assetManager) {
        // 打开assets下的pics/Jason目录
        AAssetDir* dir = AAssetManager_openDir(assetManager, "pics/Jason");
        if (dir == nullptr) {
            std::cerr << "Failed to open assets dir: pics/Jason" << std::endl;
            return;
        }
    
        const char* filename = nullptr;
        // 遍历目录下所有文件
        while ((filename = AAssetDir_getNextFileName(dir)) != nullptr) {
            // 这里可以处理每个文件,比如加载纹理资源
            std::cout << "Found file in assets: pics/Jason/" << filename << std::endl;
            
            // 如需打开单个文件,可使用:
            // AAsset* asset = AAssetManager_open(assetManager, ("pics/Jason/" + std::string(filename)).c_str(), AASSET_MODE_STREAMING);
            // // 读取文件内容...
            // AAsset_close(asset);
        }
    
        // 记得关闭目录句柄
        AAssetDir_close(dir);
    }
    
方案2:将assets目录复制到应用私有目录后用标准API访问

如果你的游戏逻辑严重依赖opendir这类标准C库文件API,不想大规模修改代码,可以先把assets里的pics/Jason目录复制到应用的私有文件目录(比如/data/data/你的包名/files/),之后就能像Windows上一样用opendir访问真实的文件系统路径了:

  1. 获取应用私有目录路径:

    #include <jni.h>
    #include <string>
    #include <sys/stat.h>
    
    std::string getAppPrivateDir(JNIEnv* env, jobject context) {
        jclass context_cls = env->GetObjectClass(context);
        jmethodID get_files_dir = env->GetMethodID(context_cls, "getFilesDir", "()Ljava/io/File;");
        jobject file_obj = env->CallObjectMethod(context, get_files_dir);
        
        jclass file_cls = env->GetObjectClass(file_obj);
        jmethodID get_abs_path = env->GetMethodID(file_cls, "getAbsolutePath", "()Ljava/lang/String;");
        jstring path_str = (jstring)env->CallObjectMethod(file_obj, get_abs_path);
        
        const char* path_cstr = env->GetStringUTFChars(path_str, nullptr);
        std::string result(path_cstr);
        
        // 释放局部引用
        env->ReleaseStringUTFChars(path_str, path_cstr);
        env->DeleteLocalRef(file_obj);
        env->DeleteLocalRef(file_cls);
        env->DeleteLocalRef(context_cls);
        
        return result;
    }
    
  2. 复制assets目录到私有目录:

    void copyAssetsToPrivateDir(AAssetManager* assetManager, const std::string& assets_subdir, const std::string& target_dir) {
        // 创建目标目录
        mkdir(target_dir.c_str(), 0755);
    
        AAssetDir* dir = AAssetManager_openDir(assetManager, assets_subdir.c_str());
        if (!dir) return;
    
        const char* filename = nullptr;
        while ((filename = AAssetDir_getNextFileName(dir)) != nullptr) {
            std::string asset_path = assets_subdir + "/" + filename;
            std::string target_path = target_dir + "/" + filename;
    
            // 读取assets中的文件
            AAsset* asset = AAssetManager_open(assetManager, asset_path.c_str(), AASSET_MODE_STREAMING);
            if (!asset) continue;
    
            // 写入到私有目录
            FILE* out_file = fopen(target_path.c_str(), "wb");
            char buffer[1024];
            int bytes_read = 0;
            while ((bytes_read = AAsset_read(asset, buffer, sizeof(buffer))) > 0) {
                fwrite(buffer, 1, bytes_read, out_file);
            }
            fclose(out_file);
            AAsset_close(asset);
        }
    
        AAssetDir_close(dir);
    }
    
  3. 使用示例:
    复制完成后,就可以用标准API访问了:

    // 假设已经获取到JNIEnv* env、jobject context和AAssetManager* assetManager
    std::string private_dir = getAppPrivateDir(env, context) + "/pics/Jason";
    copyAssetsToPrivateDir(assetManager, "pics/Jason", private_dir);
    
    // 现在可以正常使用opendir了
    DIR* dir_ptr = opendir(private_dir.c_str());
    if (dir_ptr != nullptr) {
        // 遍历目录逻辑...
        closedir(dir_ptr);
    }
    
额外注意事项
  • 大小写敏感:Android的文件系统是严格区分大小写的,而Windows不是,要确认代码中的路径(比如pics/Jason)和assets目录的实际大小写完全一致,别出现Jason写成jason的情况。
  • 路径格式:assets中的路径不要带开头的斜杠,"pics/Jason"是正确写法,带斜杠反而会导致找不到目录。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:16:02