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/7/25 23:14:11 阅读更多 →
LinkedIn资料完整,为什么还是很少收到招聘人员联系?|蒸汽求职分享

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

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

2026/7/25 23:14:10 阅读更多 →
本地大模型部署优化:解决Hermes Agent卡顿与显存溢出问题

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

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

2026/7/25 23:13:10 阅读更多 →

最新新闻

使用gimme构建高效Go开发工作流:专家级建议

使用gimme构建高效Go开发工作流:专家级建议

使用gimme构建高效Go开发工作流:专家级建议 【免费下载链接】gimme Install go, yay! 项目地址: https://gitcode.com/gh_mirrors/gi/gimme gimme是一款轻量级Go版本管理工具,能够帮助开发者快速安装、切换和管理多个Go环境,让Go开发工…

2026/7/25 23:22:14 阅读更多 →
Shadcn Admin Kit实战案例:从零构建完整客户管理系统

Shadcn Admin Kit实战案例:从零构建完整客户管理系统

Shadcn Admin Kit实战案例:从零构建完整客户管理系统 【免费下载链接】shadcn-admin-kit Component kit to build your Admin app with shadcn/ui 项目地址: https://gitcode.com/gh_mirrors/sh/shadcn-admin-kit Shadcn Admin Kit是一套基于shadcn/ui的组件…

2026/7/25 23:22:14 阅读更多 →
Jellium Desktop视频截图技巧:捕捉精彩瞬间的方法

Jellium Desktop视频截图技巧:捕捉精彩瞬间的方法

Jellium Desktop视频截图技巧:捕捉精彩瞬间的方法 【免费下载链接】jellium-desktop An unofficial desktop client for Jellyfin 项目地址: https://gitcode.com/GitHub_Trending/je/jellium-desktop Jellium Desktop是一款非官方的Jellyfin桌面客户端&…

2026/7/25 23:22:14 阅读更多 →
2023年最值得尝试的窗口管理器:kiwmi入门指南与核心功能解析

2023年最值得尝试的窗口管理器:kiwmi入门指南与核心功能解析

2023年最值得尝试的窗口管理器:kiwmi入门指南与核心功能解析 【免费下载链接】kiwmi A fully programmable Wayland Compositor 项目地址: https://gitcode.com/gh_mirrors/ki/kiwmi kiwmi是一款完全可编程的Wayland窗口管理器(Compositor&#x…

2026/7/25 23:22:14 阅读更多 →
深入API Sprout源码:Go语言实现高性能OpenAPI 3 Mock Server的原理

深入API Sprout源码:Go语言实现高性能OpenAPI 3 Mock Server的原理

深入API Sprout源码:Go语言实现高性能OpenAPI 3 Mock Server的原理 【免费下载链接】apisprout Lightweight, blazing fast, cross-platform OpenAPI 3 mock server with validation 项目地址: https://gitcode.com/gh_mirrors/ap/apisprout API Sprout是一款…

2026/7/25 23:22:14 阅读更多 →
生产级RAG系统构建指南:从架构设计到性能优化

生产级RAG系统构建指南:从架构设计到性能优化

1. 项目概述作为一名经历过无数次RAG系统踩坑的老程序员,我深知新手在构建生产级RAG系统时的迷茫。今天这份指南,就是要带大家避开那些我当年掉过的坑,从零开始搭建一个真正能扛住线上流量的RAG系统。RAG(Retrieval-Augmented Gen…

2026/7/25 23:21:14 阅读更多 →

日新闻

突破文档下载限制:kill-doc让你看到的都能保存

突破文档下载限制:kill-doc让你看到的都能保存

突破文档下载限制:kill-doc让你看到的都能保存 【免费下载链接】kill-doc 看到经常有小伙伴们需要下载一些免费文档,但是相关网站浏览体验不好各种广告,各种登录验证,需要很多步骤才能下载文档,该脚本就是为了解决您的…

2026/7/25 0:00:35 阅读更多 →
C++ string类模拟实现:从深拷贝到内存管理的完整指南

C++ string类模拟实现:从深拷贝到内存管理的完整指南

1. 项目概述:为什么我们要“手撕”string类?在C的学习道路上,尤其是从C语言过渡到C的“初阶”阶段,string类绝对是一个绕不开的核心。标准库里的std::string用起来太方便了,、find、substr,几个操作符和函数…

2026/7/25 0:00:35 阅读更多 →
三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

1. 先搞清楚“三角洲寻宝鼠”到底是什么工具从名称来看,“三角洲寻宝鼠”更像是一个资源查找或文件检索类工具,而不是游戏或娱乐软件。这类工具的核心价值在于帮助用户快速定位特定资源,比如文档、图片、压缩包或特定格式的文件。如果你经常需…

2026/7/25 0:00:35 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/25 5:08:22 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/25 5:13:53 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/24 18:52:18 阅读更多 →

月新闻