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 构建器与组件复用

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

2026/9/20 20:21:10 阅读更多 →
LinkedIn资料完整,为什么还是很少收到招聘人员联系?|蒸汽求职分享

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

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

2026/9/14 11:40:58 阅读更多 →
本地大模型部署优化:解决Hermes Agent卡顿与显存溢出问题

本地大模型部署优化:解决Hermes Agent卡顿与显存溢出问题

在本地部署大模型运行 Hermes Agent 时,很多开发者都会遇到卡顿、响应变慢甚至显存溢出的问题。这些问题不仅影响开发效率,更让本地 AI 应用的体验大打折扣。本文将从硬件配置、模型选择、参数调优到系统优化,为你提供一套完整的解决方案。 无论你是刚接触 Hermes Agent 的…

2026/9/22 4:17:07 阅读更多 →

最新新闻

锂电池能修复吗?老电工聊透最佳实践避坑指南

锂电池能修复吗?老电工聊透最佳实践避坑指南

锂电池能修复吗?老电工聊透最佳实践避坑指南 看了一堆教程还是不会写项目?别急,这行干久了都知道,纸上谈兵和现场实操是两码事。很多同行问我,手里的锂电池鼓包、续航跳水,到底还能不能救?网上说法不一,今天咱们不整虚的,直接拆解这套“检测-评估-…

2026/9/23 4:23:52 阅读更多 →
2026最新卢路避坑指南:3个致命错误让你白干

2026最新卢路避坑指南:3个致命错误让你白干

2026最新卢路避坑指南:3个致命错误让你白干 版本升级后 API 全变了,代码跑不起来,日志一片红,这是 2026 年开发者最头疼的噩梦。 你花三天调通的功能,换个环境直接崩溃,明明本地没问题,上线就报错。…

2026/9/23 4:23:52 阅读更多 →
电商用户行为分析实战:从数据清洗到漏斗模型的完整链路

电商用户行为分析实战:从数据清洗到漏斗模型的完整链路

1. 别急着写代码:一套大数据分析项目的正确打开方式我见过太多人拿到大数据分析任务,第一反应就是打开Jupyter Notebook,急着去跑pd.read_csv()。结果呢?跑了三天,分析出了二十多张图表,业务方问一句“所以…

2026/9/23 4:23:52 阅读更多 →
3个坑:cf魔笛高频面试题,手写实现避坑指南

3个坑:cf魔笛高频面试题,手写实现避坑指南

3个坑:cf魔笛高频面试题,手写实现避坑指南 版本升级后 API 全变了,是不是让你抓狂?很多 高频面试题 直接考的就是这种底层机制,稍不留神就掉坑里。今天不聊虚的,直接拆解 cf魔笛…

2026/9/23 4:23:52 阅读更多 →
Qwen2-7B-Int4本地部署实战指南

Qwen2-7B-Int4本地部署实战指南

我无法根据您提供的输入内容生成符合要求的博文。原因如下:输入中缺少项目标题所必需的核心要素:该标题“年薪千万诱惑?阿里通义千问负责人周畅跳槽!字节跳动再添一员大将”属于典型的社会新闻类传播信息,本质是人物职…

2026/9/23 4:23:52 阅读更多 →
3个坑让你少熬2夜,副词修饰副词速查手册

3个坑让你少熬2夜,副词修饰副词速查手册

3个坑让你少熬2夜,副词修饰副词速查手册 配置环境就卡半天?别慌,我懂这种对着终端发呆的感觉。刚入行那会儿,我为了搞懂一个“副词修饰副词”的逻辑,在CSDN上翻了二十多页帖子,结果发现核心就在那三行代码里。今天这份速查手册,就是帮你把这种“…

2026/9/23 4:22:52 阅读更多 →

日新闻

3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析

3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A…

2026/9/23 0:00:23 阅读更多 →
2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我

2k显示屏性能优化踩坑:版本升级后API全变了,这份源码解析救了我 刚把开发环境的显示器从1080P换到2K,跑老项目直接报错,版本升级后 API…

2026/9/23 0:01:25 阅读更多 →
3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点

3步搞定美眉图实战项目,告别官方文档抓不住重点 官方文档翻了三遍还是云里雾里?别急,美眉图在实战项目中常被用来做数据可视化,但它的原理比你想的简单。今天咱们直接上手,用一个完整的小项目把美眉图跑通,不再死磕那些冗长的理论说明。…

2026/9/23 0:01:25 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/22 4:32:41 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/22 4:38:57 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/22 8:51:04 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/21 15:36:51 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/21 15:36:51 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/22 2:43:42 阅读更多 →