当前位置: 首页 > article >正文

避坑指南:Android 10分区存储下File API失效的5种替代方案

Android 10分区存储适配实战5种File API替代方案详解当你的应用在Android 10设备上突然开始崩溃控制台不断抛出File.mkdir() failed: EACCES (Permission denied)之类的错误时作为开发者可能会感到措手不及。这正是分区存储Scoped Storage带来的直接影响——传统文件操作API在这个新机制下大面积失效。本文将带你直击问题核心用可立即落地的代码方案解决这些燃眉之急。1. 分区存储的核心变化与问题定位Android 10引入的分区存储机制彻底改变了应用访问外部存储的方式。我们过去习以为常的File类操作现在会在这些常见场景中失效尝试在公共目录如DCIM、Download创建文件夹时mkdirs()返回false使用FileOutputStream写入非应用专属目录时抛出FileNotFoundException通过绝对路径访问媒体文件时获取不到真实内容跨应用文件共享功能突然中断关键限制对比表操作类型Android 9及之前Android 10分区存储模式公共目录创建文件需WRITE权限完全禁止访问其他应用媒体文件需READ权限需用户通过SAF授权非媒体文件访问直接可用必须使用SAF应用私有目录访问自由访问仅限当前应用实际测试发现即使在manifest声明了requestLegacyExternalStorage某些厂商ROM仍会强制启用分区存储。最稳妥的方式还是进行完整适配。2. MediaStore全流程解决方案2.1 媒体文件写入标准流程替代传统的new File(Environment.getExternalStorageDirectory(), test.jpg)方式现在应该使用MediaStore API// 插入图片到系统相册 ContentValues values new ContentValues(); values.put(MediaStore.Images.Media.DISPLAY_NAME, demo.jpg); values.put(MediaStore.Images.Media.MIME_TYPE, image/jpeg); values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES); Uri uri getContentResolver().insert( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values ); try (OutputStream out getContentResolver().openOutputStream(uri)) { Bitmap bitmap BitmapFactory.decodeResource(getResources(), R.drawable.demo); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out); }关键参数说明RELATIVE_PATH限定在Pictures、Movies等特定子目录MIME_TYPE必须与文件实际类型匹配DISPLAY_NAME不含路径的文件名2.2 媒体文件查询优化技巧当需要查询设备上的图片时避免使用File.listFiles()改用String[] projection { MediaStore.Images.Media._ID, MediaStore.Images.Media.DISPLAY_NAME, MediaStore.Images.Media.DATE_ADDED }; Cursor cursor getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null, MediaStore.Images.Media.DATE_ADDED DESC ); // 使用CursorLoader替代直接查询以获得更好性能3. Storage Access Framework深度应用3.1 目录授权完整流程对于需要访问特定目录的场景如文档管理类应用使用SAF的目录授权// 启动目录选择器 Intent intent new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); intent.addFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION ); startActivityForResult(intent, REQUEST_CODE); // 在onActivityResult中处理 Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode REQUEST_CODE resultCode RESULT_OK) { Uri treeUri data.getData(); // 持久化保存权限 getContentResolver().takePersistableUriPermission( treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION ); // 使用DocumentFile操作文件 DocumentFile pickedDir DocumentFile.fromTreeUri(this, treeUri); DocumentFile newFile pickedDir.createFile(text/plain, note.txt); } }3.2 文件操作兼容方案创建DocumentFile工具类处理各种文件操作public class FileUtils { public static boolean createFile(Context context, Uri treeUri, String mimeType, String fileName) { DocumentFile dir DocumentFile.fromTreeUri(context, treeUri); if (dir ! null dir.canWrite()) { DocumentFile file dir.createFile(mimeType, fileName); return file ! null; } return false; } public static boolean deleteFile(Context context, Uri treeUri, String fileName) { DocumentFile dir DocumentFile.fromTreeUri(context, treeUri); DocumentFile file dir.findFile(fileName); return file ! null file.delete(); } }4. 应用专属存储空间利用4.1 外部私有目录最佳实践对于应用专属文件优先使用getExternalFilesDir()// 获取应用专属图片目录 File imagesDir new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), app_images); if (!imagesDir.exists()) { imagesDir.mkdirs(); // 在私有目录仍可用传统File API } // 写入私有文件 File privateFile new File(imagesDir, config.json); try (FileWriter writer new FileWriter(privateFile)) { writer.write(jsonData); }目录类型对照环境变量对应目录是否需要权限DIRECTORY_MUSICAndroid/data//Music否DIRECTORY_PODCASTSAndroid/data//Podcasts否DIRECTORY_DOWNLOADSAndroid/data//Download否4.2 缓存文件处理策略临时文件应使用缓存目录系统可能在存储不足时自动清理File cacheFile new File(getExternalCacheDir(), temp.tmp); try { // 创建缓存文件 if (!cacheFile.exists()) { cacheFile.createNewFile(); } // 使用后及时删除 cacheFile.deleteOnExit(); } catch (IOException e) { e.printStackTrace(); }5. 混合模式下的兼容方案5.1 版本判断与降级处理实现版本自适应逻辑public static boolean isScopedStorageEnabled(Context context) { if (Build.VERSION.SDK_INT Build.VERSION_CODES.R) { return !Environment.isExternalStorageLegacy(); } else if (Build.VERSION.SDK_INT Build.VERSION_CODES.Q) { return !context.getApplicationInfo().targetSdkVersion Build.VERSION_CODES.Q || !context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EXTERNAL_STORAGE_LEGACY); } return false; } // 使用示例 if (isScopedStorageEnabled(this)) { // 使用MediaStore/SAF } else { // 使用传统File API }5.2 关键API兼容封装创建统一的文件操作接口public interface FileOperator { Uri createFile(String mimeType, String displayName) throws IOException; InputStream openInputStream(Uri uri) throws IOException; OutputStream openOutputStream(Uri uri) throws IOException; } // 实现MediaStore版本 public class MediaStoreFileOperator implements FileOperator { // 实现具体方法... } // 实现传统File版本 public class LegacyFileOperator implements FileOperator { // 实现具体方法... } // 工厂方法 public static FileOperator createFileOperator(Context context) { return isScopedStorageEnabled(context) ? new MediaStoreFileOperator(context) : new LegacyFileOperator(context); }在项目实际开发中我们遇到一个典型场景用户拍摄的照片需要同时保存到公共相册和应用私有目录。通过组合使用MediaStore和传统File API最终实现方案如下// 保存到公共相册 Uri publicUri saveToMediaStore(bitmap); // 同时保存到私有目录 File privateFile new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), backup_ System.currentTimeMillis() .jpg); try (FileOutputStream fos new FileOutputStream(privateFile)) { bitmap.compress(Bitmap.CompressFormat.JPEG, 95, fos); }这种双写策略既满足了系统规范要求又保证了应用自身的数据可靠性。

相关文章:

避坑指南:Android 10分区存储下File API失效的5种替代方案

Android 10分区存储适配实战:5种File API替代方案详解 当你的应用在Android 10设备上突然开始崩溃,控制台不断抛出"File.mkdir() failed: EACCES (Permission denied)"之类的错误时,作为开发者可能会感到措手不及。这正是分区存储&…...

FedProx实战:如何用Python在异构网络中优化联邦学习(附代码)

FedProx实战:Python实现异构网络联邦学习优化指南 联邦学习作为分布式机器学习的前沿分支,正面临两大核心挑战:设备间的系统异构性(计算与通信能力差异)和数据分布的统计异构性(non-IID数据)。本…...

解锁论文写作新姿势:书匠策AI,你的期刊论文智囊团

在学术的浩瀚海洋中,每一位探索者都渴望拥有一盏明灯,照亮前行的道路。对于广大教育领域的学者、研究生乃至本科生而言,撰写一篇高质量的期刊论文不仅是学术能力的体现,更是通往更高学术殿堂的钥匙。然而,面对繁琐的选…...

Win10主机与Win7虚拟机共享文件夹超详细指南(VMware/虚拟机新手必看)

Win10主机与Win7虚拟机无缝共享文件夹全流程解析 刚接触虚拟机的用户经常会遇到一个棘手问题:如何在主机和虚拟机之间高效传输文件?复制粘贴受限、U盘来回插拔效率低下,而共享文件夹功能正是解决这一痛点的最佳方案。本文将手把手带你完成从零…...

别再让你的Druid监控裸奔了!手把手教你配置账户密码与访问控制

Druid监控安全加固实战:从零构建企业级防护体系 在Java生态中,Druid作为阿里巴巴开源的数据库连接池,凭借其强大的监控功能成为众多企业的标配组件。但令人担忧的是,超过60%的生产环境存在Druid监控页面暴露的安全隐患——这相当于…...

2026年最好的AI创业机会,就藏在你压根看不上的角落里

还在焦虑AI会替代你?抢你饭碗?你根本不知道,现在有一群人,正在用AI给自己“印钞票”他们不是搞什么ChatGPT插件,也不是训练大模型,他们就盯着那些看着不起眼,甚至你压根看不上的小事。利用这些小…...

从ResNet到ASPP:手把手教你用PyTorch复现DeepLabv3+的Encoder模块(含代码详解)

从ResNet到ASPP:手把手教你用PyTorch复现DeepLabv3的Encoder模块(含代码详解) 在语义分割领域,DeepLabv3以其出色的性能和清晰的架构设计成为众多研究者和工程师的首选方案。本文将带您深入探索其核心组件——Encoder模块的实现细…...

LeRobot数据采集全流程解析:从环境配置到动作回放(SO-100实战)

LeRobot数据采集全流程实战:从环境搭建到动作复现的SO-100深度指南 当我们需要让机器人学会新技能时,数据采集是构建智能系统的第一步。LeRobot作为Hugging Face推出的机器人学习平台,通过标准化流程降低了开发门槛。本文将带你完整走通SO-10…...

如何通过哈氏训练提升孩子的学习能力以应对多动症表现和作业拖延症?

如何运用哈氏训练助力孩子克服多动症表现与作业拖延 哈氏训练是一种有效的应对策略,尤其对有多动症表现和作业拖延症的孩子。首先,这种训练方法可以帮助孩子建立稳定的日常作息,提高他们的注意力和自我控制能力。通过结构化的活动和渐进式的任…...

3个高效步骤:DriverStore Explorer解决Windows驱动管理难题

3个高效步骤:DriverStore Explorer解决Windows驱动管理难题 【免费下载链接】DriverStoreExplorer Driver Store Explorer 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer 问题诊断:驱动管理中的隐形痛点 当你打开设备管理器…...

从平台束缚到自由聆听:ncmdump如何让加密音乐重获新生?

从平台束缚到自由聆听:ncmdump如何让加密音乐重获新生? 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 你是否曾经遇到过这样的困境?在某个音乐平台精心收藏的歌单,却无法在车载音响上…...

大模型解决方案专家,火山方舟:用大模型赋能企业,成本、效果、落地难题一网打尽!

火山方舟作为大模型解决方案专家,依托豆包大模型家族及智能模型路由等技术,打造企业级服务平台。核心价值在于解决模型效果、推理成本、落地难度三大挑战。提供更强模型能力、更低成本推理、更易落地应用三大解决方案,助力企业高效落地AI应用…...

Token火了,一文读懂词元经济产业链

“词元(Token)是新的大宗商品。”在英伟达2026年度开发者大会(GTC)上,英伟达创始人兼CEO黄仁勋首次提出词元经济。 黄仁勋提出一个公式:收入每瓦词元数可用千兆瓦数。他解释称,数据中心如今已经…...

NSSCTF做题记录十 | [巅峰极客 2022 决赛]开端:strangeTempreture

[巅峰极客 2022 决赛]开端:strangeTempreture随便点击一个流量包,右击点击追踪流,TCP 流把这几个字母拼接到一起,下面还有很多ZmxhZ3s5N2JmZWIwMy1mYTVjLWFhNmYtYWQxZS05YzVkMzhjNzQ0OWV9base64 解码,得到 flagflag{97…...

别再只用Chat了!深度挖掘Cursor的‘规则’与‘上下文’功能,打造你的专属AI编程助手

解锁Cursor的隐藏力量:从代码助手到项目级智能架构师 在AI编程工具爆发的时代,大多数开发者仅仅停留在基础对话和代码补全的层面。但Cursor的真正价值远不止于此——它能够成为你项目架构的智能协作者、团队规范的自动化执行者,以及复杂工程问…...

低空经济落地第一站:工业无人机巡检的格局重构、技术革命与黄金增长期

在海拔4500米的青藏高原特高压输电线路上,一架全自主工业无人机沿着预设航线平稳飞行,以厘米级精度悬停在绝缘子旁,红外热成像镜头精准捕捉到导线的微小发热点,端侧AI大模型实时完成缺陷识别与风险分级,数据同步回传至…...

Agentic SOC:AI原生时代,安全运营的终极范式革命

2026年RSAC全球网络安全大会上,一个现象级的行业转折正在发生:全场超过90%的主流安全厂商将核心展位与重磅发布聚焦于Agentic SOC,全球500强企业中超过62%已启动相关试点,21%完成了核心生产环境的规模化落地。与之形成强烈对比的是…...

别急着重装!Stable Diffusion WebUI安装失败后,如何利用现有文件快速恢复(Mac/Windows通用)

别急着重装!Stable Diffusion WebUI安装失败后,如何利用现有文件快速恢复(Mac/Windows通用) 当你兴致勃勃地准备体验Stable Diffusion WebUI的强大功能时,突然在安装过程中遇到错误提示,那种挫败感可想而知…...

Spring Boot项目必备:用Arthas实现MyBatis Mapper热加载的完整配置流程

Spring Boot项目必备:用Arthas实现MyBatis Mapper热加载的完整配置流程 在持续交付的微服务架构中,开发团队经常面临一个共同挑战:每次修改MyBatis的Mapper XML文件后,都需要重启服务才能验证变更效果。这种低效的反馈循环严重拖慢…...

【大数据】离线数仓核心组件:Hive 架构解析与进阶操作指南

Hive 是基于 Hadoop 的数据仓库工具,主要用于解决海量结构化日志的数据统计问题。它提供了一套类 SQL 的查询语言 HiveQL,通过将 SQL 语句转换为运行在 Hadoop 集群上的 MapReduce 或 Spark 任务,大幅降低了大数据分析的工程门槛。 目录 一、…...

Halcon轮廓拟合与排序:从基础算子到工业检测实战

1. Halcon轮廓处理技术概览 在工业视觉检测领域,轮廓处理技术扮演着至关重要的角色。想象一下,你站在一条自动化产线旁,传送带上快速移动着各种形状的金属零件。这些零件可能摆放得杂乱无章,表面可能有划痕或油污,但生…...

从MIMO到相控阵:深入浅出聊聊RFSoC的MTS(多片同步)为啥是5G/雷达系统的核心

从MIMO到相控阵:深入浅出聊聊RFSoC的MTS(多片同步)为啥是5G/雷达系统的核心 在5G Massive MIMO基站的天线阵列背后,或是军用雷达的相控阵天线系统中,数以百计的射频收发通道需要像精密交响乐团般协同工作——任何微小的…...

STM32CubeMX + EG2131预驱芯片:搞定无刷电机六步换向的硬件配置避坑指南

STM32CubeMX与EG2131预驱芯片的无刷电机六步换向实战解析 引言 在嵌入式电机控制领域,无刷直流电机(BLDC)因其高效率、长寿命和低维护成本等优势,正逐步取代传统有刷电机。然而,当工程师们从理论转向实践时&#xff0c…...

多图拼长条与宫格拼接批处理备忘

手头有一批产品白底图,需要批量产出两类物料:一类是横向四连图做详情对比,一类是 22 宫格做缩略封面。统一用【批量图片拼接工具】走完,下面只记参数组合和踩坑点,不写实现细节。输入侧是「主文件夹」路径,…...

WPF高性能绘图避坑指南:为什么你的心电图曲线会让CPU飙升?

WPF高性能绘图避坑指南:为什么你的心电图曲线会让CPU飙升? 在医疗监护设备或金融行情系统中,实时波形渲染的卡顿可能直接导致误诊或交易延迟。当你的WPF应用在绘制每秒60帧的心电图时突然出现CPU占用率突破90%,这往往不是硬件性能…...

深入解析LCD面板Gamma校准:从原理到自动化调试实践

1. Gamma校准的前世今生:从CRT到LCD的视觉革命 第一次接触Gamma校准时,我正对着两台显示器发愣——同样的设计稿在CRT显示器上色彩饱满,到了LCD屏幕却像蒙了层灰。这个困扰无数设计师的问题,背后正是Gamma值在作祟。早年的CRT显示…...

高光谱图像处理实战:5分钟搞懂Pansharpening动态卷积网络(DyPNN)原理与应用

高光谱图像处理实战:5分钟搞懂Pansharpening动态卷积网络(DyPNN)原理与应用 遥感图像处理领域近年来迎来了一项突破性技术——动态卷积网络(DyPNN)在高光谱图像融合中的应用。这项技术彻底改变了传统Pansharpening方法…...

【HALCON】test_subset_region算子实战:从原理到工业质检的精准区域嵌套检测

1. test_subset_region算子的核心原理与工业价值 在工业质检场景中,判断一个区域是否完全包含在另一个区域内,就像检查螺丝是否准确拧进了螺孔。HALCON的test_subset_region算子就是专门解决这类问题的"智能卡尺"。它的底层逻辑其实非常直观—…...

SpringBoot整合MQTT实战:从零到一构建物联网消息通信

1. 为什么选择SpringBoot整合MQTT? 物联网项目开发中,设备与服务器的通信就像快递员送货上门。MQTT协议就是这个快递员,而SpringBoot就是你家门口的智能快递柜。两者结合能让设备数据像包裹一样准时送达,还不会丢件。 我去年做过一…...

别再买成品了!手把手教你用立创EDA复刻TP4056充电板,成本不到3块钱

3元自制18650充电器:立创EDA复刻TP4056全流程实战 每次看到抽屉里闲置的18650电池,总想给它们配个充电器,但市面上的成品要么价格虚高,要么功能过剩。作为一个常年折腾电子制作的爱好者,我发现用立创EDA复刻TP4056充电…...