详解Android文件存储
摘要
其实安卓文件的操作和java在pc环境下的操作并无二致,之所以需要单独讲解是因为安卓系统提供了不同于pc的访问文件系统根路径的api,同时对一个应用的私有文件做了统一的管理。根据我的经验,初学者在这部分感到很容易混淆内部存储和外部存储两个概念。
其实安卓文件的操作和java在pc环境下的操作并无二致,之所以需要单独讲解是因为安卓系统提供了不同于pc的访问文件系统根路径的api,同时对一个应用的私有文件做了统一的管理。根据我的经验,初学者在这部分感到很容易混淆内部存储和外部存储两个概念。
相对路径和绝对路径
在java中,关于相对路径和绝对路径是这样解释的,如果你很熟悉这部分以下灰色文字可以跳过:
绝对路径是指书写文件的完整路径,例如d:\java\Hello.java,该路径中包含文件的完整路径d:\java以及文件的全名Hello.java。使用该路径可以唯一的找到一个文件,不会产生歧义。但是使用绝对路径在表示文件时,受到的限制很大,且不能在不同的操作系统下运行,因为不同操作系统下绝对路径的表达形式存在不同。
相对路径是指书写文件的部分路径,例如\test\Hello.java,该路径中只包含文件的部分路径\test和文件的全名Hello.java,部分路径是指当前路径下的子路径,例如当前程序在d:\abc下运行,则该文件的完整路径就是d:\abc\test。使用这种形式,可以更加通用的代表文件的位置,使得文件路径产生一定的灵活性。
在Eclipse项目中运行程序时,当前路径是项目的根目录,例如工作空间存储在d:\javaproject,当前项目名称是Test,则当前路径是:d:\javaproject\Test。在控制台下面运行程序时,当前路径是class文件所在的目录,如果class文件包含包名,则以该class文件最顶层的包名作为当前路径。
这是java在多数操作系统中这样操作,很显然是要我们尽可能的使用相对路径,但是在安卓中,其实多数情况下我们都是使用的绝对路径。为什么呢?注意上面说到相对路径是以当前项目所在路径为当前路径,但在安卓中我们是不可能在项目所在路径目录下做任何操作的,因为普通java中我们的项目创建于服务器(pc也算是服务器),运行于服务器,我们当然能在服务器操作自己的文件目录。但是安卓开发中,我们的项目一般是创建于自己工作的电脑,而运行于手机,既然apk已经运行于手机了,那项目就已经部署到手机上了,应该以apk在手机上的位置来确定相对路径,但我们好像们没有办法操作这个路径的,因为apk是在system目录下,就算可以操作,在这个目录下存取文件也是没有意义的,比如我写一个相册程序,图片肯定是放在外部存储中,而如果我要保存一个应用的一些设置数据,我是放在内部存储的data目录下,因此其实在安卓文件管理中,我们都是在操作绝对路径。
File类
操作一个文件(读写,创建文件或者目录)是通过File类来完成的,这个操作和java中完全一致。
Internal Storage内部存储空间
所谓的内部存储与外部存储,是指是否是手机内置。手机内置的存储空间,称为内部存储,它是手机一旦出厂就无法改变,它也是手机的硬件指标之一,通常来讲手 机内置存储空间越大意味着手机价格会越贵(很多地方把它称为手机内存,但我们做软件的知道,这并不准确,内存是指手机运行时存储程序,数据和指令的地方; 这里应该是手机内部存储的简称为内存,而并非严格意义上的内存)。
内部存储空间十分有限,因而显得可贵,所以我们要尽可能避免使用;另外,它也是系统本身和系统应用程序主要的数据存储所在地,一旦内部存储空间耗尽,手机 也就无法使用了。所以对于内部存储空间,我们要尽量避免使用。上面所谈到的Shared Preferences和下面要谈到的SQLite数据库也都是存储在内部存储空间上的。
Android本身来讲是一个Linux操作系统,所以它的内部存储空间,对于应用程序和用户来讲就是“/data/data"目录。它与其他的(外部的 存储)相比有着比较稳定,存储方便,操作简单,更加安全(因为可以控制访问权限)等优点。而它唯一的缺点就是它比较有限,比较可贵。
虽然,可以非常容易的知道程序本身的数据所在路径,所有的应用程序的数据路径都是“/data/data/app-package-name/”,所有的 程序用到的数据,比如libs库,SharedPreferences都是存放在这个路径下面。但我们在使用的时候最好不要,或是千万不要直接引用这个路 径。
使用内部存储主要有二个方式,一个是文件操作,一个是文件夹操作。无论哪种方式,Context中都提供了相应的函数来支持,使用Context不但操作 简单方便,最重要的是Context会帮助我们管理这些文件,也可以方便帮助我们控制文件的访问权限。先来系统的说下Context中关于文件和文件夹操 作的函数有哪些。
a. 创建一个文件,并打开成一个文件输出流,需要提供一个String,作为文件名
FileOutputStream output = Context.openOutputFile(filename, Context.MODE_PRIVATE); output.write(data);// use output to write whatever you like output.close();
b. 同样,想打开一个文件作为输入的话,也是只需要提供文件名
FileInputStream input = Context.openInputFile(filename); input.read(); input.close();
c. 列出所有的已创建的文件
String[] files = Context.fileList(); for (String file : files) { Log.e(TAG, "file is " + file); }
d. 删除文件,能创建就要能够删除,当然也会提供了删除文件的接口,它也非常简单,只需要提供文件名
if (Context.deleteFile(filename)) { Log.e(TAG, "delete file " + filename + " sucessfully“); } else { Log.e(TAG, "failed to delete file " + filename); }
e. 获取文件已创建文件的路径,它返回一个文件对象用于操作路径
File fileDir = Context.getFileDir(); Log.e(TAG, "fileDir " + fileDir.getAbsolutePath();
f. 创建一个目录,需要传入目录名称,它返回 一个文件对象用到操作路径
File workDir = Context.getDir(dirName, Context.MODE_PRIVATE); Log.e(TAG, "workdir " + workDir.getAbsolutePath();
g. 以File对象方式查看所创建文件,需要传入文件名,会返回文件对象
File store = Context.getFileStreamPath(filename); Log.e(TAG, "store " + store.length());
h. 获取Cache路径,无需要传入参数,返回文件对象
File cachedir = Context.getCacheDir(); Log.e(TAG, "cachedir " + cacheDir.getAbsolutePath());
总结一下文件相关操作,可以得出以下三个特点:
1. 文件操作只需要向函数提供文件名,所以程序自己只需要维护文件名即可;
2. 不用自己去创建文件对象和输入、输出流,提供文件名就可以返回File对象或输入输出流
3. 对于路径操作返回的都是文件对象。
如前所述,内部存储空间有限,可贵,安全,稳定,所以应该用来保存比较重要的数据,比如用户信息资料,口令秘码等不需要与其他应用程序共享的数据。也可以 用来创建临时文件,但一定要注意及时删除。另外,对于内部存储还有一个非常重要的特点,那就是在应用程序被卸载时,应用程序在内部存储空间的文件数据将全 部被删除。系统这样做的原因很简单,就是因为内部存储很有限,它必须保证它的可用性,因为一旦添满,系统将无法再正常工作。
External Storage外部存储空间
再来谈谈手机外部存储空间,与内部存储空间相对,外部存储 空间是指手机出厂的时候不存在,用户在使用时候可以自由添加的外部存储介质比如TS卡,SD卡等闪存储介质。这些闪存介质由最初的空间小价格贵,到现在的 大容量价格便宜,所以几乎每个支持外部存储的手机上面都有大容量(大于等于2G)的闪存卡。
Android也是不例外,它完全支持外部存储介质。其实更确切的说,它是要依赖于外部存储卡的,因为对于Android系统,如果没有外部存储卡,很多 的系统应用无法使用,比如多媒体相关的应用程序无法使用。虽然Android很依赖,但是外部存储卡也有它自身的特点,它最大的优点就是存储空间大,基本 上你可无限制的使用,也不怎么担心去清除数据。就目前来看,很多程序都在使用外部存储卡,但很少有程序去主动清理数据,所以无论你的SD卡有多大,它的可 用空间却越来越少。与内部存储不同的是,当程序卸载时,它在外部存储所创建的文件数据是不会被清除的。所以清理外部存储空间的责任丢给了用户自己,每隔一 段时间就去查看下SD卡,发现无用数据立马删除。外部存储的缺点就是不是很稳定,对于Android手机来讲可以说,很不稳定,本身闪存介质就容易出问 题,SD卡处于不能正常使用的状态十分多。
先来说说外部存储相关的使用方法和API:
a Check media availability检查介质的可用性.
如前所述,外部存储介质的稳定性十分的差,所以在使用之前一定要先检查它的可用性,如果可用再去用
final String state = Environment.getExternalStorageState(); if (state.equals(Environment.MEDIA_MOUNTED) || state.equals(Environment.MEDIA_READ_ONLY)) {// sd card is ready to us
b. Get the directory获取外部存储卡的路径
事实上,外部存储卡的路径是“/mnt/sdcard",所以你直接这样写去访问也能访问的到。鉴于可读性和可移植性的考虑,建议这样写:
File sdcardDir = Environment.getExternalStorageDirectory();
c. For API 8 or greater, there are some other useful APIs helping to manager files and directories.
如果你使用API 8(Android 2.2)或者更高,那么SDK中又多了几个操作外部存储文件和路径的接口,文档中也建议开始者更加规范的使用SD卡。比如,创建相应的目录去存储相应的数 据,Music,Picture,Video等。应用程序目录也变成了"/Android/data/package-name/data"。具体的使用 可以参考文档,这里不重复。当然,就像编程规范一样,这里只是规范,你完全可以不遵守它,但出于可读性和可移植性,还是建议按照文档建议的去做。
下面总结一下使用时应该注意的一些和外部存储的特点:
a. 外部存储卡不是随时想用就能够用的,所以一定要记得在使用之前检查它的可用性
b. 存储在外部存储卡上的数据是所有应用程序都可见,用户也可见(使用FileManager),所以安全性不是很好,虽然文档声称可以在外部存储卡上写程序 私有数据,但貌似没用,用FileManager仍然可以删除或编辑文件(Market上面的FileManager功能都十分的强大,能让用户看到SD 卡中的所有文件,和操作能看到的文件)。
c. Android手机支持把外部存储卡Mount至PC做为U盘,当连接数据线时,这时SD卡变成了U盘连接到了另外的操作系统中。什么意思,就是在 Android当中虽然有的文件属性(隐藏,私有等),到了PC上就不一定管用了,用户在PC上可以随意操作文件(这就是第二点中所提及的)。
d. 如果使用外部存储卡保存数据,一定要额外做好异常处理:外部存储卡不可用时把数据存入哪里;可用的时候再怎么同步数据(这是比较头疼的地方,可行的做法就 是当SD卡不可用时不准用户写数据,但这用户体验又不是很好,但如你所知,很多应用都这么干);你的数据被破坏了。当然常见的异常也要考虑,比如空间满 了,无法写入,磁盘坏道等。
我们还可以看一下常见的文件目录:
别忘了权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> Log.i(TAG, "getFilesDir = " + getFilesDir()); Log.i(TAG, "getExternalFilesDir = " + getExternalFilesDir("exter_test").getAbsolutePath()); Log.i(TAG, "getDownloadCacheDirectory = " + Environment.getDownloadCacheDirectory().getAbsolutePath()); Log.i(TAG, "getDataDirectory = " + Environment.getDataDirectory().getAbsolutePath()); Log.i(TAG, "getExternalStorageDirectory = " + Environment.getExternalStorageDirectory().getAbsolutePath()); Log.i(TAG, "getExternalStoragePublicDirectory = " + Environment.getExternalStoragePublicDirectory("pub_test"));
以上内容比较长,希望大家可以耐心阅读,本文所述全部都是精华,关于Android文件存储相关知识,请点击本站了解更多。