来源:
http://developer.android.com/training/basics/data-storage/files.html
Android使用了一种类似于其它平台上基于磁盘文件系统的文件系统. 本节课描述了如何使用 File API在Android文件系统中读写文件.
File 对象适用于用一种没有跳跃的从开始一直到结尾的方式读写大量数据. 例如,它很适合通过网络进行图片文件或者任何其它的文件交换.
本课程展示了如何在 你的应用中进行文件相关的基础操作. 本课程假定你熟悉Linux文件系统,还有java.io中的标准文件输入/输出操作.
所有安卓设备都有两个存储区域: "内部" 和 "外部" 存储. 这些名称来自早期的安卓, 那时候大多数设备都提供内建的非易丢失内存 (内部存储), 再加上一个可移除的存储介质,比如微型SD卡 (外部存储). 一些设备将永久存储空间分成“内部”和“外部”分区, 因此即使没有可移除的存储介质,也总会两个存储空间,而不管外部存储是不是可移除的,API行为都是一样的. 下面的列表总结的每一个存储空间的一些要点.
内部存储:
当你想要确保不管是你的用户还是其它应用都能访问你的文件,内部存储是最合适的.
外部存储:
外部存储时保存那些不需要访问限制的文件的最好地方,还有那些你想要同其它应用共享或者允许用户使用计算机来访问的文件 .
提示: 尽管应用默认被安装到内存存储, 其实你还可以在manifest中指定android:installLocation属性,那样你的应用就可以被安装在外部存储上了. 当APK的尺寸非常大,并且用户拥有一个比内存存储大得多的外部存储时,这一选择会受到欢迎. 更多的信息,可以看看 应用存储位置 .
为了写入外部存储,你必须在你的manifest文件处请求WRITE_EXTERNAL_STORAGE权限 :
<manifest ...> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ... </manifest>
注意: 当前,所有的应用都有能力不需要一个特定的权限就可以读取内部存储. 不过,这将会在将来的版本中改变. 如果你的应用需要读取内部存储(但不去写入它), 那么你将会需要声明 READ_EXTERNAL_STORAGE 权限. 为了确保你的应用能如预期的运作, 在变化还没有起作用之前,你现在就应该声明这一权限.
<manifest ...> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> ... </manifest>
不过,如果你的应用使用了 WRITE_EXTERNAL_STORAGE 权限,那么它也就隐含了读取内部存储的权限了 .
在内部存储上保存文件不需要任何权限. 你的应用程序总是有在其内部存储目录中读写文件的权限.
当要在一个内部存储中保存一个文件时,你可以通过调用下面两个方法的其中之一,来获取相应的目录 文件 :
getFilesDir()
返回一个表示你应用的内部路径的 File .
getCacheDir()
返回一个表示你应用的临时缓存内部路径的 File . 要确保一旦文件不再需要时都删除一次,并且在任何给定时间你使用的内存都有一个合理的大小限制, 比如 1MB. 如果系统开始低存储消耗的运行 , 他可能在没有提示就删除了你的缓存文件.
为了在这些目录中的一个里面创建一个新的文件,你可以使用 File() 构造器,传入由上述指定了你的内部存储路径的方法提供的 File . 例如:
File file = new File(context.getFilesDir(), filename);
此外,你还可以调用 openFileOutput() 来获得一个 FileOutputStream , 它会在你的内部路径中写入一个文件 . 例如,这里是如何将一些文本写入一个文件 :
String filename = "myfile"; String string = "Hello world!"; FileOutputStream outputStream; try { outputStream = openFileOutput(filename, Context.MODE_PRIVATE); outputStream.write(string.getBytes()); outputStream.close(); } catch (Exception e) { e.printStackTrace(); }
或者,如果你需要缓存一些文件,你就应该换用 createTempFile(). 例如,下面的方法可以获取名称来自一个 URL 的文件,并使用这个名称在你的应用的内部缓存路径中创建一个文件 :
public File getTempFile(Context context, String url) { File file; try { String fileName = Uri.parse(url).getLastPathSegment(); file = File.createTempFile(fileName, null,context.getCacheDir()); catch (IOException e) { // Error while creating file } return file; }
注意: 你的应用的内部存储路径将用你的应用的包名,在Android文件系统的一个特殊位置指定. 技术上,如果你将文件模式设置为可读,那么其它的应用也可以读取你的内部文件. 不过,其它的应用也会需要知道你的应用的报名和文件名. 除非你明确将文件设置为可读或者可写的,其它的应用不能浏览到你的内部路径. 因此一旦你在你内部存储中的文件上使用了 MODE_PRIVATE , 它们就再也不会被其它应用访问到了.
由于外部存储可能不可用——比如用户已经将其挂载到了一台PC上,或者已经将提供外部存储的SD卡移除——你应该在访问它之前总是去验证一下其可用性 . 你可以调用 getExternalStorageState() 来检查外部存储的状态. 如果返回的状态是 MEDIA_MOUNTED, 那么你就可以读写你的文件 . 例如,下面的方法在决定存储可用性方面会很有用 :
/* Checks if external storage is available for read and write */ public boolean isExternalStorageWritable() { String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { return true; } return false; } /* Checks if external storage is available to at least read */ public boolean isExternalStorageReadable() { String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { return true; } return false; }
尽管外部存储可以被用户和其它应用修改,还是有两类文件你可以保存在这里 :
如果你想要在外部存储上保存公共的文件,使用 getExternalStoragePublicDirectory() 方法来获取一个表示外部存储上对应文件的 File . 该方法会得到一个指定你想要保存的文件类型的参数,那样它们就可以在本地同其它公共文件组织在一起,比如 DIRECTORY_MUSIC 或者 DIRECTORY_PICTURES. 例如 :
public File getAlbumStorageDir(String albumName) { // Get the directory for the user's public pictures directory. File file = new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), albumName); if (!file.mkdirs()) { Log.e(LOG_TAG, "Directory not created"); } return file; }
如果你想要保存为你应用所私有的文件,你可以通过调用getExternalFilesDir()并传入指定你想要的路径类型的名称来获取相应的路径 . 每一个通过此方法创建路径都被添加到了一个所有你的应用程序的外部存储文件的父路径 , 它们会在用户卸载你的应用时被系统删除掉 .
例如,这里有一个你可以用来为一个单独的相册创建一个路径的方法 :
public File getAlbumStorageDir(Context context, String albumName) { // Get the directory for the app's private pictures directory. File file = new File(context.getExternalFilesDir( Environment.DIRECTORY_PICTURES), albumName); if (!file.mkdirs()) { Log.e(LOG_TAG, "Directory not created"); } return file; }
如果之前定义的子路径名称没有一个适合你的文件,你可以调用 getExternalFilesDir() 并传入null. 这回返回外部存储上你的应用的私有路径的根路径 .
请记住当用户卸载你的应用时,getExternalFilesDir() 在一个路径中创建的路径都会被删除掉 . 如果你想要存储的文件在用户卸载你的应用后还能用 — 诸如当你的应用是一个照相机,而用户想要保留这些照片时 — 你应该换用 getExternalStoragePublicDirectory().
不管你是使用文件可以被共享的 getExternalStoragePublicDirectory() 或者文件为你的应用所私有的 getExternalFilesDir() , 你所使用的由API提供像DIRECTORY_PICTURES这样的常量都很重要 . 这些路径名称确保了文件为系统正常对待 . 例如,存储在 DIRECTORY_RINGTONES 中的文件可以被系统的媒体搜索器归为铃声一类,而不是音乐 .
如果你事先知道要保存多少数据,你就可以通过调用 getFreeSpace() 或者 getTotalSpace() 发现是否有足够空间保存这些数据,而不会导致一个 IOException . 这些方法分别提供了存储卷中当前有多少可用空间以及总空间. 这种信息在避免填充的数据量超过一定的阈值时也同样有用 .
不过,系统并不能确保你可以写入同 getFreeSpace() 所获取到的剩余空间大小同等量的数据. 如果返回的数量比你想要保存的数据多几个MB,或者如果文件系统占率低于90%,那么往往还算安全。否则,你可能就不应该再往里面写入了.
注意: 你并不一定要在保存你的文件之前检查剩余空间的数量. 你可以尝试首先写入文件,然后获取一个 IOException ,如果这个异常发生了的话 . 如果你并不知道你需要多少空间的时候,可能就得这么做 . 例如,如果你在保存文件之前改变了文件的编码方式,将一张PNG图片转换成了JPG的,你是不会事先知道文件的大小的 .
你应该总是删除你不再需要的文件。删除一个文件最直接的方式是让打开的文件引用自身调用 delete() .
myFile.delete();
如果文件被保存在外部存储上,你也可以通过调用deleteFile()叫 Context来定位并删除一个文件 :
myContext.deleteFile(fileName);
注意: 当用户卸载你的应用时,Android系统会删除下面这些东西 :
不过,你应该定期手动的去删除使用 getCacheDir() 创建的缓存文件,也要有规律的去删除你不再需要的文件.