Android平台调用opendir()返回目录不存在的问题求助
我之前在移植C++游戏到Android时也踩过这个坑,你遇到的问题核心原因其实很好理解:Android的assets目录并不是文件系统中的真实路径——Windows上你的资源是直接放在磁盘目录里的,所以opendir这类标准C库文件API能正常访问,但Android的assets是打包在APK内部的资源容器,没有对应的文件系统路径,用普通文件API自然会返回ENOENT(目录不存在)的错误。
下面给你两种可行的解决方案,你可以根据自己的代码架构选择:
方案1:用Android NDK的AssetManager API遍历assets目录
这是最推荐的原生适配方式,不需要修改太多游戏逻辑,直接利用Android提供的资源访问接口:
先获取
AssetManager实例:
如果你的JNI代码是从Java层调用的,可以直接把AssetManager作为参数传入;如果用的是NativeActivity框架,也可以通过ANativeActivity的assetManager成员直接获取。用
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访问真实的文件系统路径了:
获取应用私有目录路径:
#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; }复制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); }使用示例:
复制完成后,就可以用标准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
相关产品推荐
相关产品推荐

