应用的文件如何保存在android手机中

应用在android手机中有两个与自己应用先关的包名文件夹,这两个文件夹的名称就是你自己应用的包名,在文章后面统一称为包名文件夹

1. /data/data/{packageName}

这是应用在android内部存储的文件夹,在app启动后回自动创建。这个文件夹主要存储数据库文件,SharePreference文件等其他需要较高安全级别的文件,这个文件在一般状态下是无法通过手机的文件浏览器查看的,只有在root之后可以查看。因此我们可以在这儿文件夹下面存放一些我们不希望用户能轻易修改的文件。

2. /storage/emulated/0/Android/data/{packageName}

SD卡目录,早期的android手机由于内部存储比较小,因此不适合存储较大的媒体文件和缓存,因此有了SD卡这个东西。这个路径主要是存储一些媒体缓存文件,安全级别要求不高,我们可以利用手机的文件浏览器进行查看,同时也可以对文件进行操作。这个目录同样会在应用启动时创建。

这两个文件夹都会随着应用的卸载而自动删除掉。对于应用开发,不建议在根目录下直接创建自定义的文件夹来保存数据,因为这样很流氓并且不符合开发规范,除非是一些设计本身就需要长期保存在本地并且不受应用自身限制的数据,比如说用户主动保存到手机的图片等。

/data/data/{packageName}的介绍

这个包名文件一般从文件使用的角度来说,我们应用的非常的少,所以只做简要介绍。注意不同的手机路径可能不一定完全相同。

路径获取

我们可以调用Environment.getDataDirectory()方法获取到第一级/data/的路径。这时,一定要注意,很多同学喜欢直接用字符串拼接的方式来拼接路径, 但这种方式不规范代码健壮性也不够完全。但是相应的,谷歌爸爸也没有给我们能够获取应用本身包名路径的API(这不是坑爹么),我们能用的只有Context.getDir(String name, int mode)这个方法。所以,我们可以调用这个方法在这个路径下创建一个子目录,然后获取该目录的父级目录就得到了包名文件夹的完整路径。

多用户的路径区别

上文中说道了不要手动拼接路径更重要的原因,就在于/data/data/不是绝对的。在android4.2之后开始加入了多用户的支持,这使得/data/data/这个路径发生了变化。

在单用户系统下,包名文件夹路径一般为:/data/data/{packageName}

在多用户系统下,包名文件夹路径一般为:/data/user/0/{packageName},其中/0表示的时第一个用户。

/storage/emulated/0/Android/data/{packageName} SD卡路径的介绍

到了比较重要的SD卡路径的介绍了。回想当时,作者也是个什么都不懂的低端码农,而和我搭档的哥们儿一上来对着文件浏览器一阵乱点,然后告诉我这个就是SD卡路径。当时我就想,这都什么年代了,手机出厂还自带SD卡的?还有,这特么为什么有两个包名文件夹啊?然而这哥们儿也是似懂非懂,一个劲儿得给重复我解释这是SD卡,这是数据路径…

对于两个包名路径的问题,相信大家读到这儿基本上已经明白两者的区别了。下面解答一下为什么现在的手机还有SD卡路径这个问题。

其实这也是一个历史遗留问题了。当初的android手机内部存储都非常的小,因此很依赖外部SD卡来存储媒体文件,这也是这个目录的由来。后来的手机内部存储空间越做越大,可是这些手机没有SD卡,如何兼容以前旧手机数据下生成的SD卡路径呢?因此,在后续的android手机中,就在手机内部存储中模拟出了一个SD卡路径,这也就是为什么手机明明没有SD卡,却依然有SD卡的路径。我们可以调用Environment.getExternalStorageState()这个方法来查看SD卡的状态,只要返回的状态与Environment.MEDIA_MOUNTED的值相同,就代表SD卡当前状态是可用的。

Environment.getExternalStorageState()

无论在有真实物理SD卡或者只有模拟SD卡的状态下,只要处于可用状态,Environment.getExternalStorageState()理论上都会返回Environment.MEDIA_MOUNTED。

Environment.isExternalStorageEmulated()

上文说了,我们是无法通过SD卡的状态值来判断是否真的有SD卡的。那么,怎么判断到底有没有真实的物理SD卡呢?这就需要用到Environment.isExternalStorageEmulated()这个方法了,相信一看到方法名大家就知道这是干嘛的了。这里顺便多扯一句,emulated这个单词很可能在你的SD卡路径里面也会出现,是模拟的意思,当你看到你的SD卡路径出现这个词时,说明这个路径很大概率是模拟的SD卡路径。

需要注意这个路径在不同的手机路径也可能不一定完全相同。

包名文件夹何时创建

上文已经提到,我们在应用启动时会自行创建,因此这两个文件夹不需要我们手动去创建。同时,需要注意的时,在Application生命周期中,这个两个文件夹就会自动创建,而这时我们如果开始在包名文件夹内创建目录,则有可能造成父级目录不存在的情况。在我的应用场景中,之前的程序员在Application一开始的onCrate中,就把包名文件夹下需要用到的目录一股脑门的创建了出来,这样在低版本手机上经常出现包名文件夹创建不成功的情况,这时就算手动去创建包名文件夹仍然会创建失败,但在文件浏览器中也看不到。所以建议在底层的文件方法中统一采取不信任原则,即当我们要使用文件路径时都假设改路径可能不存在,需要进行路径合法性验证。如果是文件,要判断文件的父级目录和文件本身是否存在;如果是文件夹,则只需要判断文件夹本身是否存在。

应用包名文件夹无法成功创建的问题

这是上文提到的我在自身的应用场景中遇到的问题,在低版本手机(Android4.4及以下)中出现的概率更高,表现就是在应用安装后,SD卡目录下没有创建对应的应用包名文件夹,而自己手动创建也是返回文件夹创建失败,进而后续的所有文件操作都会失败从而造成严重的bug。目前尚未找到是什么原因,不负责任的猜测可能是由什么东西占用了文件读写资源而由得不到释放,因为只要重启手机这个问题就没了。所以我的解决方案是在一进入应用时就对文件系统进行验证,判断手机的SD卡可用状态以及SD卡路径下包名文件夹是否存在,如果不存在则弹窗强制提醒用户退出应用并重启手机。