一、分区存储来了,传感器数据文件怎么办?
Android 10 开始,Google 搞了个大动作——分区存储(Scoped Storage)。
说白了,就是不让 App 随便乱翻别人的文件夹了。以前那种直接往 /sdcard 根目录写文件的日子,一去不复返。
我刚开始接触这个变化时,心里也犯嘀咕:传感器数据文件,比如光线传感器的日志、校准参数,这些该往哪放?
嗯,今天我们就来聊聊这个。
1.1 分区存储到底改了啥?
先看一张图,帮你快速理解核心变化:
你看,左边是旧模式,App 权限很大。右边是新模式,App 被圈在自己的「沙箱」里。
对于传感器数据来说,这意味着什么?
- 不能再往公共目录乱写文件了——比如
/sdcard/sensor_log.txt,系统会直接拒绝。 - 但 App 自己的私有目录不受影响——
context.getExternalFilesDir()依然可以正常读写。 - 如果非要共享数据——得用 MediaStore 或者 SAF(存储访问框架)。
1.2 传感器数据文件的典型场景
我在项目中遇到过这样一个需求:App 需要每隔 100ms 采集一次光线传感器数据,保存到 CSV 文件,方便后续分析。
在 Android 10 之前,代码大概是这样的:
// ❌ 旧方式,Android 10+ 会报错
File file = new File(Environment.getExternalStorageDirectory(), "light_sensor.csv");
FileOutputStream fos = new FileOutputStream(file);
// 写入数据...
fos.close();
这段代码在 Android 10 以上会抛出 FileNotFoundException,因为根目录不让写了。
那正确的做法是什么?
核心原则:传感器数据文件,优先放在 App 专属目录。
1.3 适配方案一:使用 App 专属目录
这是我最推荐的方式。简单、稳定、不用申请额外权限。
// ✅ 推荐方式:使用 App 专属外部存储目录
File sensorDir = context.getExternalFilesDir("sensor_data");
if (sensorDir != null && !sensorDir.exists()) {
sensorDir.mkdirs();
}
File file = new File(sensorDir, "light_sensor_" + System.currentTimeMillis() + ".csv");
FileOutputStream fos = new FileOutputStream(file);
// 写入传感器数据...
fos.close();
你想想看,这个目录对应的是 /sdcard/Android/data/你的包名/files/sensor_data/。
好处很明显:
- 不需要
READ_EXTERNAL_STORAGE或WRITE_EXTERNAL_STORAGE权限 - App 卸载时,系统会自动清理这些文件
- 其他 App 默认访问不到,数据安全
小提示:我个人习惯在 onCreate 里先检查一下目录是否存在,不存在就创建。避免在写入时才发现目录没了。
1.4 适配方案二:使用 MediaStore(适合共享场景)
如果传感器数据需要导出给其他 App 使用,比如分享给数据分析工具,那可以用 MediaStore。
// 使用 MediaStore 保存到 Downloads 目录
ContentValues values = new ContentValues();
values.put(MediaStore.Downloads.DISPLAY_NAME, "light_sensor.csv");
values.put(MediaStore.Downloads.MIME_TYPE, "text/csv");
values.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/SensorData");
Uri uri = contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values);
OutputStream os = contentResolver.openOutputStream(uri);
// 写入传感器数据...
os.close();
这里要注意:
- Android 10 上,
RELATIVE_PATH是新增的字段,用来指定子目录 - 不需要 WRITE_EXTERNAL_STORAGE 权限,但需要
WRITE_EXTERNAL_STORAGE在 Android 10 上已经废弃了 - 读取自己写入的文件,不需要额外权限
曾经踩过的坑:我在一个项目里用 MediaStore 写文件,结果发现文件名重复时,系统会自动加后缀(比如 "light_sensor (1).csv")。后来我每次写入前先检查一下同名文件是否存在,或者用时间戳命名,避免冲突。
1.5 适配方案三:使用 SAF(存储访问框架)
如果用户想自己选择保存位置,比如保存到某个特定文件夹,那就用 SAF。
// 启动 SAF 文件选择器
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/csv");
intent.putExtra(Intent.EXTRA_TITLE, "light_sensor.csv");
startActivityForResult(intent, REQUEST_CODE_SAVE_FILE);
// 在 onActivityResult 中获取 Uri
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_SAVE_FILE && resultCode == RESULT_OK) {
Uri uri = data.getData();
// 通过 ContentResolver 写入数据
OutputStream os = getContentResolver().openOutputStream(uri);
// 写入传感器数据...
os.close();
}
}
这种方式用户体验最好,但代码稍微复杂一些。适合「导出」功能。
1.6 三种方案对比
| 方案 | 适用场景 | 权限要求 | 文件可见性 | 推荐指数 |
|---|---|---|---|---|
| App 专属目录 | 内部使用,不共享 | 无 | 仅本 App | ⭐⭐⭐⭐⭐ |
| MediaStore | 需要共享给其他 App | 无(Android 10+) | 系统媒体库可见 | ⭐⭐⭐⭐ |
| SAF | 用户自定义保存位置 | 无 | 用户指定位置 | ⭐⭐⭐ |
1.7 关于 Android 11 的补充
Android 11 进一步收紧了权限。如果你还想用 MANAGE_EXTERNAL_STORAGE 权限来绕过分区存储,Google 审核会非常严格。
我的建议是:别折腾了,老老实实用上面三种方案。
对于传感器数据这种场景,App 专属目录基本够用。除非你有特别强的理由,否则别去申请 MANAGE_EXTERNAL_STORAGE。
一句话总结:Android 10+ 的分区存储,对传感器数据文件的影响就是——别再往公共目录乱写了。用 getExternalFilesDir() 或者 MediaStore,既合规又省心。
好了,关于分区存储对传感器数据文件的影响,就聊到这里。下一节我们聊聊如何在不同 Android 版本上做兼容处理。