Android Studio中使用NDK的C++无法打开外部.npy文件求助
解决NDK C++无法读取外部存储.npy文件的问题
针对你遇到的无法打开/storage/emulated/0/data.npy文件的问题,核心原因是Android权限机制和存储策略的限制,以下是具体解决步骤:
1. 补充动态权限申请
Android 6.0(API 23)及以上版本,仅在AndroidManifest.xml声明权限是不够的,必须在代码中动态向用户申请危险权限。在你的MainActivity中添加权限申请逻辑:
import android.Manifest; import android.content.pm.PackageManager; import android.os.Bundle; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; public class MainActivity extends AppCompatActivity { private static final int REQUEST_STORAGE_PERM = 100; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); checkStoragePermission(); } private void checkStoragePermission() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_STORAGE_PERM); } else { // 权限已授权,调用JNI方法 getInt(); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_STORAGE_PERM) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { getInt(); } else { Toast.makeText(this, "需要存储权限才能读取文件", Toast.LENGTH_SHORT).show(); } } } // JNI方法声明 public native int getInt(); }
2. 适配Android 10+的分区存储(Scoped Storage)
Android 10(API 29)引入了分区存储机制,直接访问外部存储根目录的文件会被限制,可通过以下方式解决:
方式一:临时兼容旧存储模式
在AndroidManifest.xml的<application>标签中添加属性,禁用分区存储:
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:requestLegacyExternalStorage="true" <!-- 添加这行 --> android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Demo"> ... </application>
注意:该属性在Android 11(API 30)仍可使用,但Android 13+建议使用其他合规方式。
方式二:使用应用专属外部存储
将data.npy移动到应用专属外部存储目录(无需权限),路径为/storage/emulated/0/Android/data/com.example.demo/files/,C++代码中修改路径:
std::string fname = "/storage/emulated/0/Android/data/com.example.demo/files/data.npy";
在Java层可通过getExternalFilesDir(null)获取该目录路径,避免硬编码。
3. 排查文件本身问题
- 确认
/storage/emulated/0/data.npy文件确实存在,可通过系统文件管理器查看。 - 检查文件是否被其他应用锁定,或者文件权限是否异常(比如仅创建者可访问)。
4. 优化C++代码的异常处理
JNI默认不处理C++异常,直接抛出会导致程序崩溃,建议改为通过JNI抛出Java异常:
#include <stdio.h> #include <jni.h> #include <string> extern "C" JNIEXPORT jint JNICALL Java_com_example_demo_MainActivity_getInt(JNIEnv* env, jobject /* this */) { std::string fname = "/storage/emulated/0/data.npy"; FILE* fp = fopen(fname.c_str(), "rb"); if(!fp) { // 抛出Java IOException jclass ioException = env->FindClass("java/io/IOException"); env->ThrowNew(ioException, ("无法打开文件: " + fname).c_str()); env->DeleteLocalRef(ioException); return -1; } // 后续读取npy文件的逻辑... fclose(fp); return 0; }
内容的提问来源于stack exchange,提问作者terdev
相关产品推荐
相关产品推荐

