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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 14:13:12