数据存储,是我们在Android开发中经常遇到的场景,数据的存储方式也有多种方式,例如文件存储、数据库存储、网路存储等等。无论哪种存储,都会涉及到手机的存储空间,而其中比较绕人的就是Android手机的内部存储和外部存储了,这两天花了点时间总结了一番,这里写成博客权当记录。
概念:注意内部存储不是内存,它是手机中的一块存储区域,是系统本身和系统应用程序主要的数据存储所在地。手机的内部存储通常不会很大,一旦手机的内部存储容量被用完,可能会出现手机无法使用的情形。对于开发者来说,不宜存储视频等大文件,适和存储一些小文件。比如我们常常用的SharedPreferences和SQLite数据库都是存储在内部存储中的,不会占用太大的空间。其主要的api如下:
//获取手机内部存储空间的绝对路径。
Environment.getDataDirectory().getAbsolutePath();
//获取当前应用包名文件夹下的files文件夹
getFilesDir().getAbsolutePath();
//获取当前应用包名文件夹下cache文件夹
getCacheDir().getAbsolutePath();
//在内部存储空间内创建(或打开现有的)目录。
getDir(String name, MODE_PRIVATE);
这么看太苍白,截一张我手机的图解释下:
通常我们手机安装了一个app,都会在data/data目录下新增一个我们应用包名的文件夹,而数据库,SharedPreferences文件都是存放在这个文件夹中,getFilesDir是用来获取这个文件夹下的files文件夹路径(如果存在的话),getCacheDir则是获取cache文件夹路径的。至于Environment.getDataDirectory()获取到的路径是这样的:
就是最外层的/data目录,也就是我们所说的内部存储所在的根目录。内部存储没什么好说的,对于一个app来说,无非就是data/data目录下应用包名所在的文件夹。这里不禁想起来一个问题,似乎我们买手机,手机参数从来都没告诉我们内部存储有多大,我们常说的64g,128g可能只是外部存储或者是内部存储和外部存储的总和,无论你怎么搜索,都不会有关于一部手机内部存储的信息的,那我们如何获取一部手机的内部存储大小呢?作为Android程序员,当然是有固定的api给我们用啦,看代码:
public static long getTotalInternalMemorySize() {
File path = Environment.getDataDirectory();
StatFs stat = new StatFs(path.getPath());
//获取内部存储中每个区块的存储大小。
long blockSize = stat.getBlockSize();
//获取内部存储中所有的区块数量。
long totalBlocks = stat.getBlockCount();
//返回内部存储空间的总大小。
return totalBlocks * blockSize;
}
我的手机是小米6 64g版本,看下我的手机内部存储空间有多大:
当然这个是字节数。55425040384/1000/1000/1000得到的GB大小,值为52.8个GB。有人会问了,出去系统占用的内存,这不是和手机宣传的64g差不多么?不应该是SD卡,属于外部存储么?当然,现在的手机大部分都不再支持外接SD卡了,内部存储和外部存储都是在一起的,但是概念上做了区分,内部存储和外部存储是一块存储介质上的不同区域。而以前外接SD卡则是在硬件上做了区分(Android 4.4以前),手机自带的存储卡就是内部存储,外接的SD卡就是外部存储。其实本质上没有区别,只是现在的手机都一体化了,再暴力拆解不符合现在的美学原理,也可以说是时代进化的产物吧~
说完了内部存储,咱们再来聊一聊外部存储。外部存储,首先可以明确一点的是,如果现在的手机还能外接SD卡,那么外接的SD卡一定是外部存储。但是一部手机可能自己本身就区分了内部存储和外部存储,这个时候又外接一个SD卡,不是有两个外部存储了么?是的,理论上是可以有两个外部存储。存储方式都一样,关键是如何去区分这两个外部存储,看代码:
File[] files;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
files = getExternalFilesDirs(Environment.MEDIA_MOUNTED);
for(File file:files){
Log.e("main",file);
}
}
getExternalFilesDirs可以获取多个外部存储空间,返回一个数组。这样就可以得到手机自身所带的外部存储和外接SD卡所定义的外部存储了。我这里没有可以插SD卡的手机,大家如果有可以插SD卡的手机,可以自行试一试。外部存储常用的api:
//获取手机外部存储的路径:/storage/emulated/0
Environment.getExternalStorageDirectory().getAbsolutePath();
//获取某种特定内容,如视频、照片、警报、铃声、音乐所在的路径,传入的参数是内容类型。 /storage/emulated/0/DCIM
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM ).getAbsolutePath();
//获取某个应用在外部存储中的files路径 /storage/emulated/0/Android/data/demo.suning.com.demo/files
getExternalFilesDir("").getAbsolutePath();
//获取某个应用在外部存储中的cache路径 /storage/emulated/0/Android/data/demo.suning.com.demo/cache
getExternalCacheDir().getAbsolutePath();
获取特定内容的类型有以下几种:
DIRECTORY_ALARMS //警报的铃声
DIRECTORY_DCIM //相机拍摄的图片和视频保存的位置
DIRECTORY_DOWNLOADS //下载文件保存的位置
DIRECTORY_MOVIES //电影保存的位置, 比如 通过google play下载的电影
DIRECTORY_MUSIC //音乐保存的位置
DIRECTORY_NOTIFICATIONS //通知音保存的位置
DIRECTORY_PICTURES //下载的图片保存的位置
DIRECTORY_PODCASTS //用于保存podcast(博客)的音频文件
DIRECTORY_RINGTONES //保存铃声的位置
当然还有其他的一些api,这里就不一一列举了,google爸爸起名字还是比较见名思义的,看api名称大体可以猜到是干嘛用的。
问:getExternalFilesDir和刚刚内部存储中的getFilesDir有什么区别呢和共同点呢?
区别:一个时在外部存储中,一个是在内部存储中。
相同点:路径中都带有当前app的包名,表明是当前app的专属文件。当app卸载或手动清除某个应用数据,它们也会被清除掉,其实它们的作用都是为了管理app的数据,让每一个app都有一个自己专属的文件夹,这样方便于系统管理,避免因为文件随意存储导致的存储空间混乱;另外就是为了当卸载应用时会把这些文件删除,不至于应用不在了,却还留下一堆垃圾文件。
问:既然内部存储和外部存储都会有当前app的专属文件夹,那我们应该优先用哪个呢?
答:理论上是优先使用外部存储,防止手机因为内部存储耗尽导致手机无法使用。当然一些无关紧要的小文件也可以存储在内部存储中,诸如数据库,SharedPreferences,大文件优先于外部存储。
当然使用之前可能需要做一下判断,是否有外部存储,如果没有,则使用内部存储,有的话才使用外部存储:
public static String getFilePath(Context context, String dir) {
String directoryPath = "";
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {//判断外部存储是否可用
directoryPath = context.getExternalFilesDir(dir).getAbsolutePath();
} else {//没外部存储就使用内部存储
directoryPath = context.getFilesDir() + File.separator + dir;
}
File file = new File(directoryPath);
if (!file.exists()) {//判断文件目录是否存在
file.mkdirs();
}
return directoryPath;
}
关于内部存储和外部存储的关系,我想引用另一篇博客的一张图:
参考:https://blog.csdn.net/u010937230/article/details/73303034,这里盗的图,如果作者看到了还希望作者多多包涵~
至于Android每个版本SD卡的路径都不一样的问题,这里就不去分析了,管他google怎么玩,反正我们有api,只要api不变,我们就可以玩得转。所谓它自皮来它自恶,我自一口真气足。不过有时间和有兴趣的小伙伴可以研究研究哈,毕竟钻研是一种学习精神,是一种学习态度。而我很懒不想钻研~