Android模拟器写入CSV报ENOENT错误解决及导出电脑方法
Android模拟器写入CSV报ENOENT错误修复及文件导出到本地方法
核心报错
运行时触发文件找不到异常,关键日志如下:
W/System.err: java.io.FileNotFoundException: /storage/emulated/0/Notes/realtive_angle.csv: open failed: ENOENT (No such file or directory)
错误原因
- 未适配Android版本存储权限规则:Android 6.0(API 23)及以上危险权限需要动态申请,仅在清单文件声明不生效;Android 10(API 29)及以上默认开启分区存储,未做兼容配置的情况下无法直接在外部存储根目录创建文件夹和文件
- 代码无创建结果校验:调用
mkdirs()、创建文件时没有判断返回结果,目录创建失败后直接初始化FileWriter就会触发ENOENT异常 - 写入逻辑存在缺陷:原代码未添加CSV分隔符、换行符,且漏写了z轴数据,就算写入成功也不符合CSV格式规范
修复方案
1. 配置清单文件权限
在AndroidManifest.xml中添加对应权限声明,做版本兼容:
<!-- API 32及以下版本读写外部存储权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <application <!-- 开启Android 10及以上版本旧版存储模式兼容 --> android:requestLegacyExternalStorage="true" ...> <!-- 原有Activity等配置 --> </application>
更推荐的无权限方案:如果不需要其他应用访问生成的CSV,直接存到应用私有外部存储目录即可,不需要申请任何权限,获取目录方式为
getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS),对应路径为/storage/emulated/0/Android/data/你的应用包名/files/Documents,不会触发权限拦截。
2. 添加动态权限申请逻辑
在执行文件写入前,先校验是否拿到存储权限,未授权则先向用户申请:
private static final int STORAGE_PERM_CODE = 1001; private static final String TAG = MainActivity.class.getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 校验存储权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, STORAGE_PERM_CODE); return; } // 有权限直接执行写入 writeCsvToFile(); } // 处理权限申请结果 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == STORAGE_PERM_CODE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { writeCsvToFile(); } else { Toast.makeText(this, "请授予存储权限后再生成CSV文件", Toast.LENGTH_SHORT).show(); } } }
3. 修正文件写入逻辑
增加目录、文件创建结果校验,补全CSV格式逻辑:
private void writeCsvToFile() { String csvFileName = "realtive_angle.csv"; double[] relative_x = {1,2,3}; double[] relative_y = {4,5,6}; double[] relative_z = {7,8,9}; // 公共目录写法(需要权限) File rootDir = new File(Environment.getExternalStorageDirectory(), "Notes"); // 私有目录写法(不需要权限,替换上面一行即可) // File rootDir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS); // 校验目录创建结果 if (!rootDir.exists()) { boolean createDirRes = rootDir.mkdirs(); if (!createDirRes) { Log.e(TAG, "目录创建失败,请检查权限配置"); return; } } File targetCsv = new File(rootDir, csvFileName); try { // 校验文件创建结果 if (!targetCsv.exists()) { boolean createFileRes = targetCsv.createNewFile(); if (!createFileRes) { Log.e(TAG, "CSV文件创建失败"); return; } } // 追加模式写入 FileWriter writer = new FileWriter(targetCsv, true); for (int i = 0; i < relative_x.length; i++) { // 列用逗号分隔,行尾加换行,符合CSV格式 writer.append(Double.toString(relative_x[i])) .append(",") .append(Double.toString(relative_y[i])) .append(",") .append(Double.toString(relative_z[i])) .append("\n"); } // 刷新缓冲区、关闭流 writer.flush(); writer.close(); Toast.makeText(this, "CSV写入成功,路径:"+targetCsv.getAbsolutePath(), Toast.LENGTH_LONG).show(); } catch (IOException e) { e.printStackTrace(); } }
模拟器中CSV文件导出到本地电脑的方法
- 方法1:通过Android Studio自带工具导出
- 打开Android Studio底部工具栏的
Device File Explorer - 在文件树中定位到CSV文件所在路径(公共目录为
/storage/emulated/0/Notes/,私有目录为/storage/emulated/0/Android/data/你的应用包名/files/Documents/) - 右键点击目标CSV文件,选择
Save As,选择本地电脑的存储路径完成导出
- 打开Android Studio底部工具栏的
- 方法2:通过adb命令导出
打开电脑终端,执行adb pull命令拉取文件:# 将命令中的文件路径、本地保存路径替换为你自己的实际路径 adb pull /storage/emulated/0/Notes/realtive_angle.csv D:\local_csv_save\ - 方法3:应用内分享导出
在应用中添加文件分享功能,将生成的CSV通过邮件、即时通讯工具发送到电脑端,适合非开发场景操作。
内容的提问来源于stack exchange,提问作者Aria_Z
相关产品推荐
相关产品推荐

