Android 存储体系:内部存储、SD 卡、U 盘挂载读写、工控文件持久化方案

发布时间:2026/7/25 23:14:00

Android 存储体系:内部存储、SD 卡、U 盘挂载读写、工控文件持久化方案
Android 存储体系内部存储、SD 卡、U 盘挂载读写、工控文件持久化方案工控设备的数据比女朋友还难伺候——丢了不行、路径搞错也不行、U盘没挂载就写更不行。今天就把Android存储体系给你从头捋到脚。一、Android 存储架构总览Android的存储体系可以简化成三大块存储类型路径特征是否需要权限典型用途内部存储/data/data/包名/不需要应用私有数据、数据库外部存储内部SD/storage/emulated/0/Android 10需要共享文件、日志导出可移动存储外部SD/U盘/storage/XXXX-XXXX/需要SAF或StorageManager数据备份、固件升级小提示很多人被外部存储这个名字坑了。外部存储不是指SD卡——手机自带的那个32GB/128GB存储空间在Android眼里就是外部存储External Storage它是焊接在主板上的、根本拔不掉的。真正的SD卡叫可移动存储Removable Storage。一张图理解它们的关系Android 文件系统 ├── 内部存储 (无需权限) │ ├── /data/data/com.example.industrial/ │ │ ├── files/ ← getFilesDir() │ │ ├── databases/ ← SQLite数据库 │ │ ├── shared_prefs/ ← SharedPreferences │ │ └── cache/ ← getCacheDir() │ └── /data/app/ ← APK安装位置 ├── 外部存储 (内部SD需权限) │ ├── /storage/emulated/0/ │ │ ├── Download/ │ │ ├── DCIM/ │ │ └── Android/data/com.example.industrial/ │ │ ├── files/ ← getExternalFilesDir() │ │ └── cache/ ← getExternalCacheDir() └── 可移动存储 (U盘/SD卡) └── /storage/XXXX-XXXX/ ← 需要StorageManager枚举二、内部存储读写内部存储是应用专属的沙盒目录其他APP无法访问也不需要任何权限。2.1 常用路径获取方法// 1. 应用私有文件目录 - 推荐存放配置文件FilefilesDircontext.getFilesDir();// /data/user/0/com.example.industrial/files/// 2. 缓存目录 - 系统空间不足时会自动清理FilecacheDircontext.getCacheDir();// /data/user/0/com.example.industrial/cache/// 3. 数据库默认目录FiledatabaseDircontext.getDatabasePath(my_db);// /data/user/0/com.example.industrial/databases/my_db// 4. SharedPreferences目录// /data/user/0/com.example.industrial/shared_prefs/2.2 配置文件读写工控场景下设备参数采集频率、串口波特率、服务器地址等需要持久化存储。推荐方案publicclassConfigManager{privatestaticfinalStringCONFIG_FILEindustrial_config.json;// 存储到应用私有目录安全且不需要权限publicstaticvoidsaveConfig(Contextcontext,Stringjson){FileconfigFilenewFile(context.getFilesDir(),CONFIG_FILE);try(FileWriterwriternewFileWriter(configFile)){writer.write(json);}catch(IOExceptione){e.printStackTrace();}}publicstaticStringloadConfig(Contextcontext){FileconfigFilenewFile(context.getFilesDir(),CONFIG_FILE);if(!configFile.exists())returnnull;try{returnnewString(Files.readAllBytes(configFile.toPath()));}catch(IOExceptione){returnnull;}}}为什么用getFilesDir()而不是外部存储因为工控设备可能没有外置SD卡而且应用私有目录不需要权限、其他APP访问不了数据更安全。2.3 采集数据的存储策略对于大量传感器采集数据SQLite是最成熟的方案publicclassDataRepository{privateSQLiteDatabasedatabase;publicDataRepository(Contextcontext){DataDbHelperhelpernewDataDbHelper(context.getFilesDir().getAbsolutePath());databasehelper.getWritableDatabase();}publicvoidinsertSensorData(SensorDatadata){ContentValuesvaluesnewContentValues();values.put(temperature,data.temperature);values.put(humidity,data.humidity);values.put(timestamp,System.currentTimeMillis());database.insert(sensor_data,null,values);}// 定期清理旧数据防止存储爆满publicvoidcleanupOldData(longbeforeTimestamp){database.delete(sensor_data,timestamp ?,newString[]{String.valueOf(beforeTimestamp)});}}内部存储虽然安全但容量有限通常几个GB。对于长期运行且数据量大的工控设备需要定期清理或转存到外部存储。三、外部存储读写3.1 公有目录与私有目录// 公有目录 - 需要权限所有APP都能访问FiledownloadDirEnvironment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);// /storage/emulated/0/Download/// 应用私有外部目录 - Android 10不需要权限但需要用到分区存储APIFileexternalFilesDircontext.getExternalFilesDir(null);// /storage/emulated/0/Android/data/包名/files/// 应用外部缓存目录FileexternalCacheDircontext.getExternalCacheDir();// /storage/emulated/0/Android/data/包名/cache/在Android 10API 29之前直接使用Environment.getExternalStorageDirectory()获取根目录就行。但从Android 10开始引入分区存储Scoped Storage强制应用使用MediaStore API或SAF框架访问共享文件。工控设备如果跑的是Android 11需要在Manifest中声明uses-permissionandroid:nameandroid.permission.MANAGE_EXTERNAL_STORAGE/然后在代码中引导用户授权if(Build.VERSION.SDK_INTBuild.VERSION_CODES.R){if(!Environment.isExternalStorageManager()){IntentintentnewIntent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);startActivity(intent);}}3.2 日志文件导出到外部存储这是工控很常见的需求——设备出了问题需要把日志导出来查看publicstaticvoidexportLogs(Contextcontext){FilelogDirnewFile(context.getExternalFilesDir(null),logs);if(!logDir.exists())logDir.mkdirs();FilelogFilenewFile(logDir,app_newSimpleDateFormat(yyyyMMdd_HHmmss,Locale.US).format(newDate()).log);try(FileWriterwriternewFileWriter(logFile)){writer.write( 工控APP运行日志 \n);writer.write(版本: BuildConfig.VERSION_NAME\n);writer.write(设备: Build.MODEL\n);writer.write(SDK: Build.VERSION.SDK_INT\n);// 写入更多日志内容...}catch(IOExceptione){e.printStackTrace();}}四、U盘挂载检测与读写工控设备经常通过U盘做数据导入导出或固件升级这是工控和普通手机APP需求差异最大的地方。4.1 U盘挂载检测U盘插入后Android系统会自动挂载到/storage/下的某个目录通常是/storage/XXXX-XXXX/格式。我们需要通过StorageManager来获取挂载信息publicclassUsbStorageManager{privateStorageManagerstorageManager;publicUsbStorageManager(Contextcontext){storageManager(StorageManager)context.getSystemService(Context.STORAGE_SERVICE);}/** * 获取所有可移动存储设备的路径 */publicListFilegetRemovableVolumes(){ListFilevolumesnewArrayList();if(Build.VERSION.SDK_INTBuild.VERSION_CODES.N){ListStorageVolumestorageVolumesstorageManager.getStorageVolumes();for(StorageVolumevolume:storageVolumes){// 判断是否可移动存储if(volume.isRemovable()volume.getState().equals(Environment.MEDIA_MOUNTED)){FilevolumeDir;if(Build.VERSION.SDK_INTBuild.VERSION_CODES.R){volumeDirvolume.getDirectory();}else{// Android 10及以下用反射获取路径volumeDirgetVolumePathReflect(volume);}if(volumeDir!null){volumes.add(volumeDir);}}}}// 兼容性方案直接扫描 /storage/FilestorageDirnewFile(/storage/);File[]filesstorageDir.listFiles();if(files!null){for(Filefile:files){Stringnamefile.getName();// U盘挂载点通常是 XXXX-XXXX 格式if(!name.equals(emulated)!name.equals(self)file.canRead()file.listFiles()!null){if(!volumes.contains(file)){volumes.add(file);}}}}returnvolumes;}SuppressWarnings(JavaReflectionMemberAccess)privateFilegetVolumePathReflect(StorageVolumevolume){try{Class?clazzvolume.getClass();Methodmethod;if(Build.VERSION.SDK_INTBuild.VERSION_CODES.N){methodclazz.getMethod(getPathFile);}else{methodclazz.getMethod(getPath);}return(File)method.invoke(volume);}catch(Exceptione){returnnull;}}}4.2 U盘文件读写有了路径就可以用标准Java File API读写U盘了。下面是一个完整的文件读写工具类publicclassUsbFileManager{/** * 从U盘读取升级文件 */publicstaticFilefindUpgradeFile(FileusbRoot){File[]filesusbRoot.listFiles();if(filesnull)returnnull;for(Filefile:files){if(file.getName().toLowerCase().endsWith(.apk)||file.getName().toLowerCase().endsWith(.zip)){returnfile;}}returnnull;}/** * 将采集数据导出到U盘 */publicstaticbooleanexportDataToUsb(FileusbRoot,StringfileName,byte[]data){FileexportDirnewFile(usbRoot,IndustrialData);if(!exportDir.exists()){exportDir.mkdirs();}FileexportFilenewFile(exportDir,fileName);try(FileOutputStreamfosnewFileOutputStream(exportFile)){fos.write(data);fos.flush();returntrue;}catch(IOExceptione){e.printStackTrace();returnfalse;}}/** * 安全弹出U盘卸载文件系统 */publicstaticbooleanunmountUsb(StorageManagerstorageManager,StorageVolumevolume){try{// 通过Intent请求卸载兼容方案// 实际卸载需要系统签名普通APP只能通知用户拔出returntrue;}catch(Exceptione){returnfalse;}}}4.3 使用 Storage Access FrameworkSAFAndroid 4.4 引入了 SAF 框架可以不申请存储权限就访问外部存储publicclassSafHelper{privatestaticfinalintREQUEST_CODE_OPEN_DIR2001;publicstaticvoidopenDirectory(Activityactivity){IntentintentnewIntent(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);activity.startActivityForResult(intent,REQUEST_CODE_OPEN_DIR);}publicstaticUrihandleResult(intrequestCode,intresultCode,Intentdata){if(requestCodeREQUEST_CODE_OPEN_DIRresultCodeActivity.RESULT_OK){UritreeUridata.getData();// 持久化权限下次启动还能用inttakeFlagsdata.getFlags()(Intent.FLAG_GRANT_READ_URI_PERMISSION|Intent.FLAG_GRANT_WRITE_URI_PERMISSION);returntreeUri;}returnnull;}}对于工控场景SAF的缺点是每次都要用户手动选择目录不适合无人值守模式。建议如果设备跑Android 10以上就用分区存储API如果跑低版本直接走文件路径更省事。五、工控文件持久化方案选择数据类型推荐方案存储位置理由配置参数SharedPreferences / JSON文件getFilesDir()数据量小读写频繁采集数据SQLite数据库getDatabasePath()支持索引、事务、条件查询图片/视频文件系统getExternalFilesDir()文件大需定期清理日志文件文件系统getFilesDir() 定期导出私有目录安全导出到U盘固件升级包文件系统U盘 / 私有目录从U盘读取校验后复制到内部路径获取方法速查表// 内部存储 context.getFilesDir();// 应用私有文件context.getCacheDir();// 应用缓存context.getDatabasePath(name);// 数据库路径context.getDir(name,MODE_PRIVATE);// 自定义目录Environment.getDataDirectory();// /data// 外部存储内部SDEnvironment.getExternalStorageDirectory();// 根目录Android 10受限context.getExternalFilesDir(null);// 应用专属外部目录context.getExternalCacheDir();// 应用外部缓存Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);// 可移动存储 // 通过StorageManager枚举无标准API六、完整代码示例U盘数据采集与导出最后给出一个工控场景的完整示例——检测U盘插入后自动导出当天的采集数据publicclassUsbDataExporterextendsBroadcastReceiver{OverridepublicvoidonReceive(Contextcontext,Intentintent){if(!UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())){return;}// 延迟2秒等待挂载完成newHandler(Looper.getMainLooper()).postDelayed(()-{exportTodayData(context);},2000);}privatevoidexportTodayData(Contextcontext){StorageManagersm(StorageManager)context.getSystemService(Context.STORAGE_SERVICE);// 枚举可移动存储ListFileusbVolumesnewUsbStorageManager(context).getRemovableVolumes();if(usbVolumes.isEmpty()){Log.w(USB,未检测到可移动存储设备);return;}FileusbRootusbVolumes.get(0);StringtodayStrnewSimpleDateFormat(yyyyMMdd,Locale.US).format(newDate());// 从数据库导出今天的采集数据ListSensorDatatodayDataqueryTodayDataFromDb(context);StringBuildercsvBuildernewStringBuilder();csvBuilder.append(时间,温度,湿度,压力\n);for(SensorDatadata:todayData){csvBuilder.append(data.toCsvLine()).append(\n);}// 写入U盘booleansuccessUsbFileManager.exportDataToUsb(usbRoot,采集数据_todayStr.csv,csvBuilder.toString().getBytes(StandardCharsets.UTF_8));Log.i(USB,数据导出(success?成功:失败));}}Android存储看着复杂其实核心就三句话私有的放getFilesDir()、共享的放getExternalFilesDir()、U盘走 StorageManager 枚举路径。记住了这三句话工控文件管理就不怕迷路了。

相关新闻

HarmonyOs应用《日记本》开发第10篇 - @Builder 构建器与组件复用

HarmonyOs应用《日记本》开发第10篇 - @Builder 构建器与组件复用

2026/7/25 23:14:00

在 ArkUI 开发中,Builder 是实现 UI 片段复用的核心装饰器。它让开发者可以将复杂的 UI 拆分为多个可维护的方法,提高代码的可读性和复用性。本篇将以日记项目的三个页面为例,深入探讨 Builder 的使用模式和最佳实践。Builder 基本语法 Build…

LinkedIn资料完整,为什么还是很少收到招聘人员联系?|蒸汽求职分享

LinkedIn资料完整,为什么还是很少收到招聘人员联系?|蒸汽求职分享

2026/7/25 23:14:00

**摘要:**LinkedIn资料完整,只能说明页面信息较齐全,并不代表招聘人员能快速判断候选人适合什么岗位。Headline、About、经历标题和项目描述需要共同传递清楚的职业方向,同时保持信息真实,并与简历和实际能力一致。 学…

为什么你的AI采集系统总在凌晨崩?揭秘内存泄漏×JS沙箱逃逸×Token轮换失效的三重连锁故障(附热修复补丁)

为什么你的AI采集系统总在凌晨崩?揭秘内存泄漏×JS沙箱逃逸×Token轮换失效的三重连锁故障(附热修复补丁)

2026/7/25 23:04:00

更多请点击: https://kaifayun.com 第一章:AI自动化 数据采集 AI驱动的数据采集正逐步取代传统手动爬取与人工录入模式,其核心在于将感知、决策与执行能力嵌入采集流程中。现代AI自动化采集系统通常融合自然语言处理(NLP&#xf…

OpenClaw本地AI助手:隐私优先的自主执行架构解析

OpenClaw本地AI助手:隐私优先的自主执行架构解析

2026/7/26 0:14:03

1. 项目概述:OpenClaw的革新理念OpenClaw项目正在重新定义个人AI助手的边界。与依赖云端服务的传统方案不同,它采用"本地优先自主执行"的双重设计理念,将数据处理和任务执行完全保留在用户设备端。这种架构带来的不仅是隐私保护层面…

2026甄选:3家宁波语文小升初机构综合评测

2026甄选:3家宁波语文小升初机构综合评测

2026/7/26 0:14:03

在宁波,每一个面临小升初、中考、高考的家庭,几乎都绕不开同一个困境——优质教育资源高度集中,但真正懂本地考情、能解决孩子个体差异的辅导机构却凤毛麟角。尤其是镇海、海曙、鄞州这些教育强区,家长们的升学焦虑并不逊于一线城…

Hackintosh终极指南:从硬件兼容到系统伪装的完整解决方案

Hackintosh终极指南:从硬件兼容到系统伪装的完整解决方案

2026/7/26 0:14:03

Hackintosh终极指南:从硬件兼容到系统伪装的完整解决方案 【免费下载链接】Hackintosh Hackintosh long-term maintenance model EFI and installation tutorial 项目地址: https://gitcode.com/gh_mirrors/ha/Hackintosh gh_mirrors/ha/Hackintosh项目是黑苹…

面试官皱眉:“RAG 答错了,你就改 Prompt、换模型、调大 Top-K?“我摇头:“连证据在哪层丢的都不知道,改参数就是碰运气“

面试官皱眉:“RAG 答错了,你就改 Prompt、换模型、调大 Top-K?“我摇头:“连证据在哪层丢的都不知道,改参数就是碰运气“

2026/7/26 0:14:03

RAG 回答错了,最常见的第一反应是什么? 改 Prompt,换大模型,或者把 Top-K 调大。 这三个动作都可能有用,但它们不应该成为排查起点。 因为最终答案只是链路最后一层。错误可能早在文档解析时就发生,也可…

大模型 Agent 三面被问:怎么解决 Skill 的依赖关系?我是这么答的

大模型 Agent 三面被问:怎么解决 Skill 的依赖关系?我是这么答的

2026/7/26 0:14:03

前段时间有个读者去面某大厂的 Agent 岗位,三面被甩出来一道题当场卡壳。题目听着挺朴素:“如果你的 Agent 里面有很多 Skill,Skill 之间还存在依赖关系的话,你打算怎么去设计来解决这个问题?” 他跟我复盘的时候说&a…

向量数据库选型实战:Milvus、FAISS与PGVector的取舍

向量数据库选型实战:Milvus、FAISS与PGVector的取舍

2026/7/26 0:04:02

引言:AI应用的基础设施之选 当企业决定搭建RAG系统或智能推荐平台时,第一个技术决策往往都是:选什么向量数据库? 这个看似基础的选择,直接影响着检索精度、系统性能和运维复杂度。2024年,向量数据库市场已经…

[具身智能-649]:个人电脑搭建 RTSP 服务完整方案(Windows / Ubuntu 双平台,适配 RDK X5 rtsp2display 调试)

[具身智能-649]:个人电脑搭建 RTSP 服务完整方案(Windows / Ubuntu 双平台,适配 RDK X5 rtsp2display 调试)

2026/7/26 0:04:02

目标:电脑作为RTSP 服务端,循环推送 H264/H265 视频流; RDK X5 通过 rtsp2display 拉流预览,完全不需要在开发板编译 live555。 提供两套成熟方案: ✅ 方案 A:FFmpeg(最简单,优先推…

PDF合并与动态水印的工程化方案:2026国内免费工具实测对比

PDF合并与动态水印的工程化方案:2026国内免费工具实测对比

2026/7/26 0:04:02

一、背景与测试方案 在实际项目交付中,PDF文件合并与版权保护水印的叠加是一个高频但容易被低估的技术需求。典型的处理链路涉及:多源PDF的文件流合并、页面级水印渲染(含透明度混合与图层叠加)、输出文件体积控制。看似简单的操作…

PDF拆分压完图糊了?2026国内免费实测,档案员都在用的组合方案

PDF拆分压完图糊了?2026国内免费实测,档案员都在用的组合方案

2026/7/26 0:04:02

说实话,提到PDF拆分再压缩,我真是被折腾得够呛。 上个月公司年度合同归档,一份300多页的PDF总合同,需要按年份拆分成三个独立文件,再分别压缩到10MB以内方便邮件发送各部门确认。我心想这还不简单?先找个海…

[具身智能-649]:个人电脑搭建 RTSP 服务完整方案(Windows / Ubuntu 双平台,适配 RDK X5 rtsp2display 调试)

[具身智能-649]:个人电脑搭建 RTSP 服务完整方案(Windows / Ubuntu 双平台,适配 RDK X5 rtsp2display 调试)

2026/7/26 0:04:02

目标:电脑作为RTSP 服务端,循环推送 H264/H265 视频流; RDK X5 通过 rtsp2display 拉流预览,完全不需要在开发板编译 live555。 提供两套成熟方案: ✅ 方案 A:FFmpeg(最简单,优先推…

PDF合并与动态水印的工程化方案:2026国内免费工具实测对比

PDF合并与动态水印的工程化方案:2026国内免费工具实测对比

2026/7/26 0:04:02

一、背景与测试方案 在实际项目交付中,PDF文件合并与版权保护水印的叠加是一个高频但容易被低估的技术需求。典型的处理链路涉及:多源PDF的文件流合并、页面级水印渲染(含透明度混合与图层叠加)、输出文件体积控制。看似简单的操作…

PDF拆分压完图糊了?2026国内免费实测,档案员都在用的组合方案

PDF拆分压完图糊了?2026国内免费实测,档案员都在用的组合方案

2026/7/26 0:04:02

说实话,提到PDF拆分再压缩,我真是被折腾得够呛。 上个月公司年度合同归档,一份300多页的PDF总合同,需要按年份拆分成三个独立文件,再分别压缩到10MB以内方便邮件发送各部门确认。我心想这还不简单?先找个海…