Android 存储目录<内部存储,外部存储app专属,外部存储公共>

首先,我们需要破除一个最大的误区: "内部存储"和"外部存储"并不是指"手机内置存储"和"SD卡" 。它们的本质区别在于安全性和可访问性

你可以用下图快速建立整体认知:

接下来,我们深入剖析图中的每一个核心概念。

内部存储 (Internal Storage)

这是指 /data/data// 目录下的空间。

  • 特点

    • 绝对私有:只有你的应用可以访问,其他应用(无Root)无法读写。用户也无法在文件管理器中直接看到。
    • 安全可靠:应用卸载时,系统会自动清除此目录下的所有文件。
    • 空间有限:与系统空间共享,通常较小。
  • 主要子目录

    • files/: 存放应用长期使用的文件。
    • cache/: 存放临时缓存文件。系统可能在存储空间不足时清理此处(但不要依赖于此,应自行管理缓存大小)。
  • 如何获取路径

kotlin 复制代码
// 获取内部 files 目录
val internalFilesDir: File = context.filesDir
// 获取内部 cache 目录
val internalCacheDir: File = context.cacheDir

外部存储 (External Storage)

这是指可以被"共享"的存储空间,通常对应设备自带的"内置共享存储"(如 /storage/emulated/0)。它进一步分为两部分:

1. 外部存储 - App专属目录

路径为:/storage/emulated/0/Android/data//

  • 特点

    • 外部但私有 :虽然位于共享存储区,但在Android 4.4及以上系统,此目录对其他应用和用户默认不可见 (无需任何权限)。应用卸载时,这里的内容也会被自动清除
    • 空间较大:使用设备的"内置共享存储"空间,通常比内部存储大得多。
    • 用途:适合存放应用专属的、较大的媒体文件或缓存。
  • 如何获取路径

kotlin 复制代码
// 获取外部 App 专属 files 目录
val externalFilesDir: File? = context.getExternalFilesDir(null)
// 获取外部 App 专属 cache 目录
val externalCacheDir: File? = context.externalCacheDir
// 可以指定子目录类型,系统会妥善管理
val externalPicturesDir: File? = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)

2. 外部存储 - 公共目录

即共享存储根目录下的标准文件夹,如 DownloadsDCIMPicturesMoviesMusic 等。

  • 特点

    • 完全共享 :所有应用和用户均可访问。文件在应用卸载后会保留
    • 需要权限:访问需要向用户申请存储权限。
  • ⚠️ 系统版本带来的关键差异(重点)

    • Android 9 (API 28) 及以下 :可以通过Environment.getExternalStoragePublicDirectory(type)直接获取路径,然后使用传统的File API进行读写。

    • Android 10 (API 29) 及以上 :引入了 Scoped Storage(分区存储) 。为了用户隐私和安全:

      1. 不能 再直接使用File路径访问公共目录(Environment.getExternalStoragePublicDirectory已废弃)。
      2. 必须通过 MediaStore API 来访问公共媒体文件(图片、视频、音频),或通过 Storage Access Framework (SAF) 系统文件选择器来访问文档和下载目录。
  • 如何获取路径/访问

kotlin 复制代码
// 方法一:通过 MediaStore 插入一张图片到公共相册 (Android 10+推荐)
val values = ContentValues().apply {
    put(MediaStore.Images.Media.DISPLAY_NAME, &#34;my_image.jpg&#34;)
    put(MediaStore.Images.Media.MIME_TYPE, &#34;image/jpeg&#34;)
    put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + &#34;/MyApp/&#34;)
}
val uri = context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
// 然后通过 ContentResolver.openOutputStream(uri) 写入数据

// 方法二:使用 Storage Access Framework 让用户选择保存位置(适用于所有文件类型)
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
    addCategory(Intent.CATEGORY_OPENABLE)
    type = &#34;application/pdf&#34;
    putExtra(Intent.EXTRA_TITLE, &#34;my_document.pdf&#34;)
}
startActivityForResult(intent, CREATE_DOC_REQUEST_CODE)

总结与建议

目录类型 路径示例 特点 适用场景 版本适配要点
内部存储 /data/data/包名/... 私有,卸载清除,空间小 敏感数据、小型数据库 基本无变化
外部-App专属 /storage/.../Android/data/包名/... 私有,卸载清除,空间大 应用专属的大文件、缓存 Android 4.4+ 行为一致,无需权限
外部-公共目录 /storage/.../Pictures/ 等 共享,卸载保留 用户希望共享的图片、文档 Android 10+必须用MediaStore或SAF

最佳实践

  1. 首选内部存储存放敏感和小型数据。
  2. 大文件、缓存优先放外部App专属目录,无需处理权限和版本兼容。
  3. 只有在需要与其他应用共享、或让用户永久保留的文件,才考虑写入公共目录,并务必做好Android 10+的Scoped Storage适配。

/data/user/0/包名/files 这个目录我可以在我手机上的文件管理里面找到?

不能直接通过普通文件管理器找到。

你提到的 /data/user/0/包名/files 这个路径,其实就是 内部存储 的另一个等价表示。它和你之前了解的 /data/data/包名/files 指向的是同一个位置。

为什么普通文件管理器看不到?

  1. 安全沙盒限制 :这个目录位于Android系统的严格保护区域。默认情况下,没有Root权限的其他应用(包括你手机上的所有文件管理器App)都无法直接访问这个路径。
  2. 用户数据隔离 :路径中的 user/0 代表的是主用户(第一个用户),这是Android系统为了支持"多用户"或"工作资料"功能而设计的目录结构。它对普通用户是透明的。

如何查看这个目录的内容?

如果你想查看这个目录里的文件,有以下几个方法:

方法 说明 适合谁
1. 通过Android Studio的 Device File Explorer 在Android Studio中,可以无需Root直接查看、导出、导入App的内部存储文件。这是开发时最常用、最方便的方法。 开发者首选
2. 使用ADB命令 在电脑上连接手机,通过命令行访问: adb shell 进入 run-as 包名 切换到应用上下文 ls /data/data/包名/files 开发者、高级用户
3. 将文件写入外部存储后查看 获得系统最高权限后,任何文件管理器都可以访问 /data 目录。但这会使设备失去保修、带来安全风险,且绝大多数日常使用场景完全不需要。 临时调试
4. Root你的手机 在Android Studio中,可以无需Root直接查看、导出、导入App的内部存储文件。这是开发时最常用、最方便的方法。 极客、深度调试者

核心结论

  • /data/user/0/包名/ 等同于 /data/data/包名/,都是内部存储,受系统严格保护。
  • 作为普通用户,你不应该、通常也无法在文件管理器中直接看到它。这是Android系统为了保护你的应用数据和隐私而设计的。
  • 作为开发者,你应该使用 Android Studio 的 Device File Explorer 来检查和操作这些文件。

如果你想测试文件是否成功写入内部存储,最简单的办法是在代码中读取它,或者按上述方法3,将其复制到一个可访问的位置再查看。

三种的文件路径<内部存储,外部app专属,外部公共,如果我需要通过微信分享其中的文件,我什么时候需androidx.core.content.FileProvider,什么时候不需要

这是一个非常好的问题,直接关系到Android文件分享功能能否正常工作。是否需要使用 FileProvider,核心判断标准是:你的文件路径是否允许接收方应用直接访问

下图清晰地展示了判断流程:

三种路径的详细分析

1. 内部存储 与 外部App专属目录 (Android 11+)

必须使用 FileProvider。

  • 原因 :这两个位置的路径(如 /data/data/你的包名/.../storage/emulated/0/Android/data/你的包名/...)对于微信等其他应用来说是不可直接访问的 。从Android 7.0 (Nougat) 开始,系统禁止直接通过 file:// URI跨应用分享私有文件,否则会抛出 FileUriExposedException
  • 作用FileProvider 会为你的私有文件创建一个临时的、只读content:// URI,并赋予接收方应用(如微信)临时的读取权限,从而安全地完成分享。

2. 外部存储 - 公共目录

通常不需要 FileProvider,可以直接使用 file:// URI。

  • 原因 :位于标准公共目录(如 Pictures/, Download/, DCIM/)的文件默认对所有应用具有读取权限,因此可以直接访问。

  • 例外情况

    • 如果你将文件保存在公共目录下你自己创建的非标准子目录 里,为了保证最大兼容性(尤其在某些定制系统上),使用 FileProvider 是更稳妥的选择。
    • 如果你需要更精细地控制权限 (例如设置URI过期时间、只允许一次访问等),也可以使用 FileProvider

使用 FileProvider 的步骤摘要

1.如果你需要用它,以下是关键步骤:

AndroidManifest.xml 中声明

xml 复制代码
    ...
    
        
    

2.创建配置文件 res/xml/file_paths.xml

这个文件定义了你想分享哪些目录下的文件。

xml 复制代码
    
    
    
     -->
    
    

3.在代码中生成 Content URI 并分享

kotlin 复制代码
// 假设你的文件位于 外部App专属目录
val fileToShare = File(context.getExternalFilesDir(null), &#34;share_image.jpg&#34;)

val contentUri: Uri = FileProvider.getUriForFile(
    context,
    &#34;${context.packageName}.fileprovider&#34;, // 必须与manifest中authorities一致
    fileToShare
)

val shareIntent = Intent(Intent.ACTION_SEND).apply {
    type = &#34;image/jpeg&#34;
    putExtra(Intent.EXTRA_STREAM, contentUri) // 使用 content:// URI
    addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // 授予临时读取权限
}
startActivity(Intent.createChooser(shareIntent, &#34;分享图片&#34;))

总结与速查表

文件位置 是否需要 FileProvider 原因 URI 类型
内部存储 必须(android11+) 其他应用无任何访问权限 content://
外部存储 - App专属目录 外部存储 - App专属目录 其他应用默认无访问权限 content://
外部存储 - 公共目录 通常不需要 其他应用有读取权限 file://

最佳实践 :如果分享功能对你很重要,且你不确定文件最终会保存在哪里,统一使用 FileProvider 是最安全、兼容性最好的做法,它几乎能处理所有情况。唯一的"缺点"是需要多写几步配置代码。

外部App专属目录和外部公共目录 (Android 11+) 这个android 11+ 有啥区别?

Android 11(API 30)对外部App专属目录的访问规则做出了一个重大变更 ,直接影响了是否需要使用 FileProvider 进行分享。

Android 11+ 的核心变化:强制执行"分区存储"

在Android 10中,分区存储(Scoped Storage)是可选 的(requestLegacyExternalStorage标志)。但从 Android 11 开始,它被强制启用,无法回退。

访问规则对比(关键区别)

访问场景 Android 10 及以下 Android 11 及以上
你的App访问自己的外部专属目录 始终可读写 始终可读写(无变化)
其他App访问你的外部专属目录 默认可访问(有权限时) 重大变化
只要其他App拥有 READ_EXTERNAL_STORAGE 权限,理论上就能扫描整个存储,包括你的 Android/data/包名/ 目录。 默认禁止访问
其他App无法直接通过文件路径访问你的专属目录,即使它拥有存储权限。
系统文件管理器访问 通常可查看(需开启"显示隐藏文件") 仍可查看(用户操作层面)

Android 11+ 下其他App如何访问(你分享文件时的影响)

由于其他App默认无法进入你的专属目录,因此你必须 通过 FileProvider 创建 content:// URI 来分享文件。这个URI本质上是一个临时的、有访问令牌的通行证

具体机制

  1. 当你的App通过 FileProvider.getUriForFile() 生成URI时,系统会"解锁"这个特定文件。
  2. 你通过 Intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 将这个"读取令牌"附加到URI上。
  3. 接收方App(如微信)在收到这个带有令牌的URI时,系统会临时允许它通过 ContentResolver 打开这个文件流,即使它根本不知道文件在 Android/data/ 下的具体路径
  4. 这个权限通常是一次性的,且仅对接收方App有效。

实际影响与代码示例

假设你要分享 Android/data/com.yourapp/files/share_image.jpg

错误做法(在 Android 11+ 上会失败)

kotlin 复制代码
val file = File(context.getExternalFilesDir(null), &#34;share_image.jpg&#34;)
// 生成 file:// URI,其他App无权访问此路径
val fileUri = Uri.fromFile(file)
shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri) // 分享失败!

正确做法(所有版本通用,Android 11+ 必须)

kotlin 复制代码
val file = File(context.getExternalFilesDir(null), &#34;share_image.jpg&#34;)

// 使用FileProvider生成安全的 content:// URI
val contentUri: Uri = FileProvider.getUriForFile(
    context,
    &#34;${context.packageName}.fileprovider&#34;,
    file
)

val shareIntent = Intent(Intent.ACTION_SEND).apply {
    type = &#34;image/jpeg&#34;
    putExtra(Intent.EXTRA_STREAM, contentUri) // 使用 content:// URI
    addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // 授予临时权限
}
// 分享成功!

总结与建议

  1. 简单结论 :自 Android 11 起,你必须使用 FileProvider 来分享位于外部App专属目录中的任何文件,因为这是其他App能访问它们的唯一合法途径。

  2. 向后兼容 :即使你的 targetSdkVersion 低于30,只要用户在Android 11+的设备上运行你的App,系统就会强制执行此规则。

  3. 最佳实践

    • 统一使用 FileProvider :为了最简单的维护和最好的兼容性,无论文件在哪里(内部、外部专属目录),也无论你的 minSdkVersion 是多少,统一使用 FileProvider 进行分享。
    • 更新你的 file_paths.xml:确保配置了正确的路径来暴露外部专属目录:
xml 复制代码
    
    
    
    

所以,回答你最初的问题: "Android 11+" 的区别就在于,从此之后,外部App专属目录真正变得和内部存储一样"私有"了,分享时必须通过 FileProvider 这个"安全检查站"。

android 10和11以后强制 使用的Scoped Storage(分区存储),到底是针对那个目录,是内部存储目录还是外部app专属目录,还是公共目录

问到了核心!Scoped Storage(分区存储)主要针对的是"外部存储-公共目录" ,但它也间接影响和强化了"外部存储-App专属目录"的私有性 。而内部存储完全不受影响

让我用一个清晰的表格和对比图来帮你理解:

Scoped Storage 管控范围一览表

存储位置 中文常称 Scoped Storage 下是否受影响? 具体变化
Internal Storage 内部存储 完全不受影响 始终私有,无需任何权限访问
External Storage - App-specific 外部App专属目录 间接受影响(访问规则被强化) 你的App :仍可自由访问。 其他App默认无法访问(Android 11+强制)。分享必须用FileProvider。
External Storage - Public Directories 外部公共目录 直接、最主要的管理目标 你的App :不能直接用File路径操作,必须通过MediaStore或SAF。 其他App:只能访问自己创建或系统授权的媒体文件。

为了更直观地展示Scoped Storage如何改变了应用访问存储的"视野",你可以参考下面的对比图:

详细解读:变化在哪里?

1. 外部存储 - 公共目录(主要改革区

  • 以前 :App只要申请了 READ_EXTERNAL_STORAGE 权限,就可以像文件管理器一样扫描、读取整个存储上所有其他App的公共文件(例如,一个图片编辑App能扫到你微信保存的所有图片),隐私风险大。

  • 现在

    • 禁止直接路径访问 :你不能再使用 new File(Environment.getExternalStoragePublicDirectory(...), ...) 这种方式。

    • 必须使用"中介API"

      • MediaStore :用于访问/创建媒体文件(图片、视频、音频)。你只能看到自己创建的文件,以及系统相册等公共集合中的文件。插入新文件时,系统会自动将其归类到对应的媒体集合。
      • Storage Access Framework (SAF) :通过系统文件选择器(一个系统UI)让用户主动选择文件或目录,适合访问文档、下载目录等非媒体文件。
    • 权限更精细 :Android 11+引入了更细粒度的权限(如 ACCESS_MEDIA_LOCATION),而不是简单的"全部或没有"。

2. 外部存储 - App专属目录(间接受影响

  • 这个目录的设计初衷就是私有的 ,Scoped Storage 只是从系统层面彻底落实了这一设计
  • Android 11+的强制规定 :其他App默认绝对无法访问 你的专属目录,连扫描都扫不到。这使得它变得和内部存储一样安全,同时也意味着分享文件时必须通过 FileProvider "开门"。

3. 内部存储(安全区,无变化

  • 完全不受Scoped Storage任何影响。它从一开始就是最私密、最安全的。

给开发者的明确指南

  1. 适配核心 :你的主要适配工作(处理 MediaStore,废弃老的 File API)都集中在需要读写公共目录的功能上。

  2. 首选存储位置

    • 私有数据 :优先放在内部存储外部App专属目录。这里不需要处理Scoped Storage的复杂规则。

    • 共享文件

      • 如果是图片/视频/音频 → 使用 MediaStore 写入对应的公共媒体目录。
      • 如果是PDF、文档等 → 使用 Storage Access Framework (SAF) 让用户选择保存位置。
  3. 权限申请

    • 访问自己的App专属目录:不需要任何权限
    • 在Android 10上,如果还需要临时以老方式访问公共目录,可以在Manifest中设置 requestLegacyExternalStorage=true(但Android 11+此标志无效)。
    • 在Android 11+上,如果App需要频繁访问设备上的所有媒体文件(如文件管理器、杀毒软件),可以申请新的 MANAGE_EXTERNAL_STORAGE 特殊权限,但上架Google Play时需要声明。

一句话总结 :Scoped Storage 就像给每个App在公共存储区建立了"专属保险柜 "(App专属目录),并设立了"公共展览馆 "(公共目录),App不能随便翻别人的保险柜,往展览馆放东西或看东西也得通过规定的流程(MediaStore/SAF)。而你的内部存储,就是你的"私人卧室",完全不变。

filepaths.xml配置介绍

FileProvider 的 `` 配置就是用来将你 App 的私有文件路径映射成 content:// URI 的"路由表"。

以下是完整详尽的配置范例,覆盖了所有你可能用到的目录:

完整的 file_paths.xml 配置模板

xml 复制代码
    

    
    
    
    
    
    
    
    
    
    

    
    
    
    
    
    
    
    
    
    
    
    

    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

    
    
    
    
        
    
    
     -->

配置详解与使用示例

1. 标签类型与路径对应关系(核心)

XML标签 对应的基准路径 典型场景
\ /data/data// 内部存储的 files 目录
\ /data/data//cache/ 内部缓存目录
\ /storage/emulated/0/ 最常用:外部存储根目录
\ Context.getExternalFilesDir() 返回的路径 外部App专属目录的父目录
\ Context.getExternalCacheDir() 返回的路径 外部缓存目录的父目录
\ Context.getExternalMediaDirs() 返回的第一个路径 外部媒体目录(API 21+)
\ / (设备的真实根目录) 极度危险,几乎用不到

2. path 属性的写法

  • &#34;.&#34; 表示匹配基准路径本身
  • &#34;subfolder/&#34; 表示匹配基准路径下的子目录(末尾的 / 可选但推荐)
  • 不能包含 ..//,也不能是绝对路径

3. 在代码中的使用示例

kotlin 复制代码
    // 分享内部存储 files 目录下的一个文件
val internalFile = File(context.filesDir, &#34;secret.txt&#34;)
val internalUri = FileProvider.getUriForFile(
    context,
    &#34;${context.packageName}.fileprovider&#34;,
    internalFile
)
// 系统会自动根据文件路径,匹配到 

// 分享外部App专属 Pictures 目录下的一张图片
val externalPicFile = File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), &#34;photo.jpg&#34;)
val externalPicUri = FileProvider.getUriForFile(
    context,
    &#34;${context.packageName}.fileprovider&#34;,
    externalPicFile
)
// 系统会自动匹配到 

重要提醒

  1. 包名占位符 :配置中的 你的应用包名 需要替换成你实际的包名(如 com.example.myapp),或者你可以用 . 表示当前目录,但显式写出包名更清晰。

  2. 按需配置 :你不需要 列出所有路径。只配置你实际会通过 FileProvider 分享文件的目录即可。更少的配置意味着更小的攻击面。

  3. 安全警告

    • 切勿使用 `` ,这将暴露设备根目录,极度危险。
    • 对于公共目录的配置,虽然列出来了,但在Android 10+上,由于Scoped Storage,你通常不会直接操作这些路径,而是通过MediaStore。这些配置主要为了兼容旧版本或特殊场景。
  4. Android 11+ 注意 :即使你为外部App专属目录配置了路径映射,其他App仍然无法直接访问 。这个配置只是为了让 FileProvider 能正确生成URI,系统仍会强制执行访问隔离。

最精简的常用配置(适合大多数App):

kotlin 复制代码
    
    
    
    

这个清单应该覆盖了你所有的使用场景。根据你的实际需求,选择必要的路径进行配置即可。

相关推荐
长安er2 小时前
LeetCode 11盛最多水的容器 & LeetCode 42接雨水-双指针2
面试·力扣·双指针·接雨水
RollingPin2 小时前
React Native与Flutter的对比
android·flutter·react native·ios·js·移动端·跨平台开发
刘大浪2 小时前
Android studio 开发将gradle 从c盘迁移至自定义盘
android·ide·android studio
a努力。2 小时前
网易Java面试被问:fail-safe和fail-fast
java·windows·后端·面试·架构
装不满的克莱因瓶2 小时前
【2026最新最全】Android Studio安装教程
android·ide·flutter·app·android studio·移动端
2501_916008892 小时前
iOS 能耗检测的工程化方法,构建多工具协同的电量分析与性能能效体系
android·ios·小程序·https·uni-app·iphone·webview
踏浪无痕3 小时前
MySQL 脏读、不可重复读、幻读?一张表+3个例子彻底讲清!
后端·面试·架构
豆苗学前端3 小时前
彻底讲透浏览器的事件循环,吊打面试官
前端·javascript·面试
柯南二号3 小时前
【大前端】【Android】获取手机的电池电量、充电状态
android