Android存储路径详细说明

一、存储的分类

Android的存储路径实际分类为

内部存储路径
内部缓存存储路径

外部存储路径
外部缓存存储路径

在有些手机上,系统也会在内部划出一个内部的SDCard路径和内部存储路径,所以当有SDCard的时候,就会有6个路径存在了

内部存储空间中的应用私有目录

对于设备中每一个安装的 App,系统都会在内部存储空间的 data/data 目录下以应用包名为名字自动创建与之对应的文件夹。这个文件夹用于 App 中的 WebView 缓存页面信息,SharedPreferences 和 SQLiteDatabase 持久化应用相关数据等。

对于没有 Root 过的手机,普通用户是无法查看 data/data 目录内容的。不过开发人员可以使用模拟器调试应用,并通过 DDMS(Dalvik Debug Monitor Server)提供的 File Explorer 工具查看模拟器设备的存储空间。

Android存储路径详细说明_第1张图片

注意:当用户卸载 App 时,系统自动删除 data/data 目录下对应包名的文件夹及其内容。

外部存储空间中的应用私有目录

考虑内部存储空间容量有限,普通用户不能直接直观地查看目录文件等其他原因,Android 在外部存储空间中也提供有特殊目录供应用存放私有文件,文件路径为:
/storage/emulated/0/Android/data/app package name

备注:一般设备都有内置 SD 卡,同时也提供外部 SD 卡拓展,可能对应路径的目录名有所差异。

值得注意的是,与内部存储空间的应用私有目录不同的是:

  • 第一,默认情况下,系统并不会自动创建外部存储空间的应用私有目录。只有在应用需要的时候,开发人员通过 SDK 提供的 API 创建该目录文件夹和操作文件夹内容。

  • 第二,自 Android 7.0 开始,系统对应用私有目录的访问权限进一步限制。其他 App 无法通过 file:// 这种形式的 Uri 直接读写该目录下的文件内容,而是通过 FileProvider 访问。

  • 第三,宿主 App 可以直接读写内部存储空间中的应用私有目录;而在 4.4 版本开始,宿主 App 才可以直接读写外部存储空间中的应用私有目录,使开发人员无需在 Manifest 文件中或者动态申请外部存储空间的文件读写权限。
    而相同点在于:同属于应用私有目录,当用户卸载 App 时,系统也会自动删除外部存储空间下的对应 App 私有目录文件夹及其内容。

同样,Android SDK 中也提供有便捷的 API 供开发人员直接操作外部存储空间下的应用私有目录:

  • getExternalFilesDir()

  • getExternalCacheDir()

等等,当然,也可以通过 Environment 类间接操作,只不过需要向用户申请操作权限:

Environment.getExternalStorageDirectory();

类似于 File 和 Cache 默认分类目录,开发人员也可以在应用私有目录中创建属于自己的自定义目录,方便于分类存储应用相关文件。

值得注意的一点是,对于外部存储空间下的应用私有目录文件,由于普通用户可以自由修改和删除,开发人员在使用时,一定要做好判空处理和异常捕获,防止应用崩溃退出!

外部存储空间中的公共目录

通常来说,应用涉及到的持久化数据分为两类:应用相关数据和应用无关数据。前者是指专供宿主 App 使用的数据信息,比如一些应用的配置信息,数据库信息,缓存文件等。当应用被卸载,这些信息也应该被随之删除,避免存储空间产生不必要的占用。

相对而言,后者更偏向于这类信息:当应用被卸载,用户仍然希望保留于设备当中的信息。常见如,拍照类应用的图片文件,用户是使用浏览器手动下载的文件等。

显然,无论是内部存储空间,还是外部储存空间,上述两个应用私有目录由于其特有的生命周期(随着应用卸载而自动清除)只适合存储应用相关数据。

或者从访问权限上来说,应用无关数据应该是宿主应用希望与其他应用共享这些数据的,应该存放在外部存储空间的公共目录文件夹下。

外部存储空间已经为用户默认分类出一些公共目录。开发人员可以通过 Environment 类提供的方法直接获取相应目录的绝对路径,传递不同的 type 参数类型即可:

Environment.getExternalStoragePublicDirectory(String type);

Envinonment 类提供诸多 type 参数的常量,比如:

  • DIRECTORY_MUSIC:Music

  • DIRECTORY_MOVIES:Movies

  • DIRECTORY_PICTURES:Pictures

  • DIRECTORY_DOWNLOADS:Download

等等,以第一个常量为例,音乐类别的公共目录绝对路径为:/storage/emulated/0/Music。如果你使用文件管理器打开设备的外部存储空间的话,均可以看到这些公共目录文件夹。

面对如此诸多的默认类别,开发人员在保存自己应用的公共文件时,也要养成良好的习惯,将要保存的数据分门别类地保存在不同公共目录下。当然,你也可以在公共目录下再次创建属于自己应用的目录,便于管理。

注意:访问外部存储空间时记得申请读写权限!

外部存储空间中的其他目录一般来说,利用两种应用私有目录和公共目录便能够存储应用中需要保存的数据和文件。如果这些还不够的话,那一定是你的开发姿势不对。在 Code Review 的前提下,如果还是不够的话,还可以在外部存储空间自由创建其他目录,通过这个方式获取外部存储空间的绝对路径,然后操作文件:

Environment.getExternalStorageDirectory();

小结

使用应用私有目录保存应用相关数据,使用公共目录保存应用无关数据(共享数据)。无论哪种情况,都需要做好数据分类保存,便于清除等统一管理。随便打开手机上的几个应用,不难发现,很多应用都包含一个清理缓存的功能。事实上,开发人员清理的就是应用相关数据,也就是应用私有目录下的文件。

考虑到外部存储空间上的内容可能被用户手动删除,或者卸载拓展 SD 卡等不可控因素,操作前记得使用 Environment 类提供的 API 方法判断容量是否充足、文件是否存在等情况,做好异常捕获,减少应用崩溃率。相信这一定是一个良好的习惯。

二、存储路径和缓存存储路径的区别

  1. 缓存路径一般用于存放一些缓存数据,当程序删除的时候,缓存文件夹也会随之删除,避免了程序在卸载之后残留大量文件。

  2. 内部缓存路径和外部缓存路径也有所区别,当系统的内存空间紧张时,内部缓存路径下的文件会被删除,但是没有一个严格的标准保障,应该对这些缓存文件占用的最大存储空间设定个最大值,当实际超过这个值时,要对这些缓存文件做相应的清理工作 。但是系统不会观察外部缓存路径下是否超出大小,要自己去做文件清理机制。

  3. 内部缓存路径默认别的程序不能访问文件,所以一些私密的数据,别的他应用是访问不了的,如果需要运行系统或者其他应用访问,可以通过修改openFileOutput()下的int mode参数让别的应用也可以访问。

三、内部缓存路径

内部的 /data/data/一般是看不到的,除非root

清除缓存

context.getCacheDir() 

模拟器:内部缓存路径:/data/data/com.mocn.testin/cache/内部缓存路径.png
Nexus5:内部缓存路径:/data/user/0/com.mocn.testin/cache/内部缓存路径.png

this .getFilesDir() 

模拟器:内部文件夹缓存路径:/data/data/com.mocn.testin/files/内部文件夹缓存路径.png

this .getDir( "文件夹名字" , Context. MODE_PRIVATE);

模拟器: 内部缓存路径新目录:/data/data/com.mocn.testin/app_文件夹名字/内部缓存路径新目录.png

this .getFileStreamPath ("下载" )

模拟器:下载:/data/data/com.mocn.testin/files/下载/下载.png

0 MODE_PRIVATE 为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下写入的内容会覆盖原文件的内容。
1 MODE_WORLD_READABLE 表示当前文件可以被其他应用读取。
2 MODE_WORLD_WRITEABLE 表示当前文件可以被其他应用写入。
3 MODE_MULTI_PROCESS SharedPreference的装载标记,设置它,文件将会在SharedPreference实例被装载到进程的时候检查是否被修改,主要用在一个应用有多个进程的情况。

清除数据会还原成最初的样子,所有的目录,sharepreferces,database文件等都会清空

四、外部存储和外部缓存的路径

1、外部缓存路径

清除缓存

context.getExternalCacheDir() 

模拟器:外部缓存路径:/storage/emulated/0/Android/data/com.mocn.testin/cache/外部缓存路径.png

Nexus5:外部缓存路径:/storage/emulated/0/Android/data/com.mocn.testin/cache/外部缓存路
径.context.getExternalFilesDir() :

外部文件夹缓存路径:/storage/emulated/0/Android/data/com.mocn.testin/files/ssssss/外部文件夹缓存路径.png

Context.getExternalFilesDir()
public File getAlbumStorageDir(Context context, String albumName) {  
// Get the directory for the app's private pictures directory.  
 File file = newFile(context.getExternalFilesDir(  
Environment.DIRECTORY_PICTURES), albumName);  
if(!file.mkdirs()) {  
Log.e(LOG_TAG, "Directory not created");  
 }  
 returnfile;  
}  

上面的代码创建了一个picture目录,并在这个目录下创建了一个名为albumName的文件,Environment.DIRECTORY_PICTURES其实就是字符串picture。

所有应用程序的外部存储的私有文件都放在根目录的Android/data/下,目录形式为/Android/data//

如果你的api 版本低于8,那么不能使用getExternalFilesDir(),而是使用Environment.getExternalStorageDirectory()获得根路径之后,自己再想办法操作/Android/data//下的文件。

也就是说api 8以下的版本在操作文件的时候没有专门为私有文件和公共文件的操作提供api支持。你只能先获取根目录,然后自行想办法。

2、外部存储路径

Environment. getExternalStorageDirectory()

模拟器:/storage/emulated/0/外部存储路径.png
Nexus5:外部存储路径:/storage/emulated/0/外部存储路径.png

Environment.getExternalStoragePublicDirectory

[java] view plain copy

1. public File getAlbumStorageDir(String albumName) {  
2. // Get the directory for the user's public pictures directory.  
3. File file = newFile(Environment.getExternalStoragePublicDirectory(  
4. Environment.DIRECTORY_PICTURES), albumName);  
5. if(!file.mkdirs()) {  
6. Log.e(LOG_TAG, "Directory not created");  
7. }  
8. returnfile;  
9. }  

在上面的代码中我们创建获得了存放picture的目录,并且新创建一个albumName文件。
如果你的api 版本低于8,那么不能使用getExternalStoragePublicDirectory(),而是使用Environment.getExternalStorageDirectory(),他不带参数,也就不能自己创建一个目录,只是返回外部存储的根路径。

在使用外部存储之前,你必须要先检查外部存储的当前状态,以判断是否可用。

1. boolean mExternalStorageAvailable = false;  
2. boolean mExternalStorageWriteable = false;  
3. String state = Environment.getExternalStorageState();  
4. if(Environment.MEDIA_MOUNTED.equals(state)) {  
5. // We can read and write the media  
6. mExternalStorageAvailable = mExternalStorageWriteable = true;  
7. } elseif(Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {  
8. // We can only read the media  
9. mExternalStorageAvailable = true;  
10. mExternalStorageWriteable = false;  
11. } else{  
12. // Something else is wrong. It may be one of many other states, but all we need  
13. //  to know is we can neither read nor write  
14. mExternalStorageAvailable = mExternalStorageWriteable = false;  
15. }  

三、getFilesDir,getExternalFilesDir,getExternalStorageDirectory,getExternalStoragePublicDirectory区别

getFilesDir,getExternalFilesDir,getExternalStorageDirectory,getExternalStoragePublicDirectory区别:

1. Log.i("codecraeer", "getFilesDir = " + getFilesDir());  
2. Log.i("codecraeer", "getExternalFilesDir = " + getExternalFilesDir("exter_test").getAbsolutePath());  
3. Log.i("codecraeer", "getDownloadCacheDirectory = " + Environment.getDownloadCacheDirectory().getAbsolutePath());  
4. Log.i("codecraeer", "getDataDirectory = " + Environment.getDataDirectory().getAbsolutePath());  
5. Log.i("codecraeer", "getExternalStorageDirectory = " + Environment.getExternalStorageDirectory().getAbsolutePath());  
6. Log.i("codecraeer", "getExternalStoragePublicDirectory = " + Environment.getExternalStoragePublicDirectory("pub_test"));  

在log中看到如下结果:

(nexus 3)上是/storage/emulated/0,奇怪的是在有些手机上同样的代码却是下面的情况:

部存储根目录为/mnt/sdcard.

参考

  • android中的文件操作详解以及内部存储和外部存储

  • 了解 Android 应用的文件存储目录,掌握持久化数据的正确姿势

你可能感兴趣的:(Android基础)