首先,我们需要破除一个最大的误区: "内部存储"和"外部存储"并不是指"手机内置存储"和"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. 外部存储 - 公共目录
即共享存储根目录下的标准文件夹,如 Downloads、DCIM、Pictures、Movies、Music 等。
-
特点:
- 完全共享 :所有应用和用户均可访问。文件在应用卸载后会保留。
- 需要权限:访问需要向用户申请存储权限。
-
⚠️ 系统版本带来的关键差异(重点) :
-
Android 9 (API 28) 及以下 :可以通过
Environment.getExternalStoragePublicDirectory(type)直接获取路径,然后使用传统的File API进行读写。 -
Android 10 (API 29) 及以上 :引入了 Scoped Storage(分区存储) 。为了用户隐私和安全:
- 不能 再直接使用
File路径访问公共目录(Environment.getExternalStoragePublicDirectory已废弃)。 - 必须通过
MediaStoreAPI 来访问公共媒体文件(图片、视频、音频),或通过Storage Access Framework (SAF)系统文件选择器来访问文档和下载目录。
- 不能 再直接使用
-
-
如何获取路径/访问:
kotlin
// 方法一:通过 MediaStore 插入一张图片到公共相册 (Android 10+推荐)
val values = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, "my_image.jpg")
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/MyApp/")
}
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 = "application/pdf"
putExtra(Intent.EXTRA_TITLE, "my_document.pdf")
}
startActivityForResult(intent, CREATE_DOC_REQUEST_CODE)
总结与建议
| 目录类型 | 路径示例 | 特点 | 适用场景 | 版本适配要点 |
|---|---|---|---|---|
| 内部存储 | /data/data/包名/... | 私有,卸载清除,空间小 | 敏感数据、小型数据库 | 基本无变化 |
| 外部-App专属 | /storage/.../Android/data/包名/... | 私有,卸载清除,空间大 | 应用专属的大文件、缓存 | Android 4.4+ 行为一致,无需权限 |
| 外部-公共目录 | /storage/.../Pictures/ 等 | 共享,卸载保留 | 用户希望共享的图片、文档 | Android 10+必须用MediaStore或SAF |
最佳实践:
- 首选内部存储存放敏感和小型数据。
- 大文件、缓存优先放外部App专属目录,无需处理权限和版本兼容。
- 只有在需要与其他应用共享、或让用户永久保留的文件,才考虑写入公共目录,并务必做好Android 10+的Scoped Storage适配。
/data/user/0/包名/files 这个目录我可以在我手机上的文件管理里面找到?
不能直接通过普通文件管理器找到。
你提到的 /data/user/0/包名/files 这个路径,其实就是 内部存储 的另一个等价表示。它和你之前了解的 /data/data/包名/files 指向的是同一个位置。
为什么普通文件管理器看不到?
- 安全沙盒限制 :这个目录位于Android系统的严格保护区域。默认情况下,没有Root权限的其他应用(包括你手机上的所有文件管理器App)都无法直接访问这个路径。
- 用户数据隔离 :路径中的
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), "share_image.jpg")
val contentUri: Uri = FileProvider.getUriForFile(
context,
"${context.packageName}.fileprovider", // 必须与manifest中authorities一致
fileToShare
)
val shareIntent = Intent(Intent.ACTION_SEND).apply {
type = "image/jpeg"
putExtra(Intent.EXTRA_STREAM, contentUri) // 使用 content:// URI
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // 授予临时读取权限
}
startActivity(Intent.createChooser(shareIntent, "分享图片"))
总结与速查表
| 文件位置 | 是否需要 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本质上是一个临时的、有访问令牌的通行证。
具体机制:
- 当你的App通过
FileProvider.getUriForFile()生成URI时,系统会"解锁"这个特定文件。 - 你通过
Intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)将这个"读取令牌"附加到URI上。 - 接收方App(如微信)在收到这个带有令牌的URI时,系统会临时允许它通过
ContentResolver打开这个文件流,即使它根本不知道文件在Android/data/下的具体路径。 - 这个权限通常是一次性的,且仅对接收方App有效。
实际影响与代码示例
假设你要分享 Android/data/com.yourapp/files/share_image.jpg:
错误做法(在 Android 11+ 上会失败) :
kotlin
val file = File(context.getExternalFilesDir(null), "share_image.jpg")
// 生成 file:// URI,其他App无权访问此路径
val fileUri = Uri.fromFile(file)
shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri) // 分享失败!
正确做法(所有版本通用,Android 11+ 必须) :
kotlin
val file = File(context.getExternalFilesDir(null), "share_image.jpg")
// 使用FileProvider生成安全的 content:// URI
val contentUri: Uri = FileProvider.getUriForFile(
context,
"${context.packageName}.fileprovider",
file
)
val shareIntent = Intent(Intent.ACTION_SEND).apply {
type = "image/jpeg"
putExtra(Intent.EXTRA_STREAM, contentUri) // 使用 content:// URI
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // 授予临时权限
}
// 分享成功!
总结与建议
-
简单结论 :自 Android 11 起,你必须使用
FileProvider来分享位于外部App专属目录中的任何文件,因为这是其他App能访问它们的唯一合法途径。 -
向后兼容 :即使你的
targetSdkVersion低于30,只要用户在Android 11+的设备上运行你的App,系统就会强制执行此规则。 -
最佳实践:
- 统一使用
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任何影响。它从一开始就是最私密、最安全的。
给开发者的明确指南
-
适配核心 :你的主要适配工作(处理
MediaStore,废弃老的FileAPI)都集中在需要读写公共目录的功能上。 -
首选存储位置:
-
私有数据 :优先放在内部存储 或外部App专属目录。这里不需要处理Scoped Storage的复杂规则。
-
共享文件:
- 如果是图片/视频/音频 → 使用
MediaStore写入对应的公共媒体目录。 - 如果是PDF、文档等 → 使用
Storage Access Framework (SAF)让用户选择保存位置。
- 如果是图片/视频/音频 → 使用
-
-
权限申请:
- 访问自己的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 属性的写法
"."表示匹配基准路径本身"subfolder/"表示匹配基准路径下的子目录(末尾的/可选但推荐)- 不能包含
..或//,也不能是绝对路径
3. 在代码中的使用示例
kotlin
// 分享内部存储 files 目录下的一个文件
val internalFile = File(context.filesDir, "secret.txt")
val internalUri = FileProvider.getUriForFile(
context,
"${context.packageName}.fileprovider",
internalFile
)
// 系统会自动根据文件路径,匹配到
// 分享外部App专属 Pictures 目录下的一张图片
val externalPicFile = File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), "photo.jpg")
val externalPicUri = FileProvider.getUriForFile(
context,
"${context.packageName}.fileprovider",
externalPicFile
)
// 系统会自动匹配到
重要提醒
-
包名占位符 :配置中的
你的应用包名需要替换成你实际的包名(如com.example.myapp),或者你可以用.表示当前目录,但显式写出包名更清晰。 -
按需配置 :你不需要 列出所有路径。只配置你实际会通过
FileProvider分享文件的目录即可。更少的配置意味着更小的攻击面。 -
安全警告:
- 切勿使用 `` ,这将暴露设备根目录,极度危险。
- 对于公共目录的配置,虽然列出来了,但在Android 10+上,由于Scoped Storage,你通常不会直接操作这些路径,而是通过
MediaStore。这些配置主要为了兼容旧版本或特殊场景。
-
Android 11+ 注意 :即使你为外部App专属目录配置了路径映射,其他App仍然无法直接访问 。这个配置只是为了让
FileProvider能正确生成URI,系统仍会强制执行访问隔离。
最精简的常用配置(适合大多数App):
kotlin
这个清单应该覆盖了你所有的使用场景。根据你的实际需求,选择必要的路径进行配置即可。