七零部落格
思则大道至简,疑则谜团重重!
思则大道至简,疑则谜团重重!
Android使用一个和其它平台基于硬盘文件系统相似的文件系统.这个课程描述了如何和在Android文件系统使用File APIs读和写文件.
一个File对象适用于读或者写从头到尾没用中断的大型数据.例如,适用于图片文件或者任何网络交换.
这节课程显示了如何在应用程序中执行基本的文件相关的任务.这个课程假设你熟悉基本的Linux文件系统和在java.io.中标准文件输入/输出APIs.
——————————————————————————————————————————————————————————————————
所有的Android设备有两个文件存储区域:”内部”和”外部”存储.这个名字来源于早期的Android,当所有设备支持内置非易失的存储(内部存储),插入一个可移除的存储媒介,例如一个micro SD卡(外部存储).一些设备分化永久的存储区域为”内部”和”外部”,所以即使没有一个可移除的存储媒介,这里总是两个存储空间和相同的API行为,无论外部存储是可移除的或者不是.下面的列表总结了关于每个存储空间的特征.
内部存储:
它总是有效的.
文件保存的地方默认仅仅你的应用程序可以访问.
当用户卸载你的应用程序的时候,系统从内部存储移除了所有你的应用的文件.
当你想确保用户即不其它应用程序也不能访问你的文件,内部存储是最好的.
外部存储:
它不总是有效的,因为用户能挂在外部存储作为USB存储,并且在某些情况下从设备中移除它.
它是全部可读的,所以在这里被保存的文件可能在你的控制之外被读取.
当用户卸载你的应用程序的时候,系统从这里仅仅移除你从getExternalFilesDir()获取目录中保存的应用的文件.
对于不要求访问限制的,和你想和其它应用程序共享的或者允许用户在电脑上访问的文件是最合适的.
提示:尽管默认情况下应用被安装在内部存储,你可以在你的清单文件中指定android:installLocation属性,以至于你的应用程序能被安装在外部存储中.用户在APK大小非常大,并且有一个比内部存储更大的外部存储的时候,添加这个选项.更多信息,请查看App Install Location.
—————————————————————————————————————————————————————————————————
为了写入额外的存储,你必须在你的清单文件中请求WRITE_EXTRAL_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_EXTRNAL_STORAGE权限,那么它暗中地包含了读取读外部存储地前线.
你不需要任何权限来在外部存储上保存文件,你的应用程序总是有权限在内部存储目录中读和写文件.
—————————————————————————————————————————————————————————————————
当在内部存储保存一个文件地时候,你可以通过调用这两个方法中的一个来以File对象获取相应的目录:
getFileDir()
返回一个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卡—在访问它之前,你总是检测有用的空间大小。你可以通过调用getExteralStorageState()方法查询外部存储状态。如果返回的状态等于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;
}
尽管外部存储被用户和其它应用程序修改,这里有两种文件类型你可能保存在这里:
公共文件
对其它应用程序和用户免费提供的文件,当用户卸载你的应用车旭,这些文件会为用户保留。例如,通过你的应用拍摄的照片,或者下载的文件。
私有文件
属于你的应用程序的文件,并且当用户卸载你的应用程序的时候应该被删除。尽管这些文件在技术上对于用户和别的应用程序是可访问的,因为他们在外部存储,他们是不向你的应用程序之外的用户扛数据的文件。
例如,通过你的应用程序被下载的额外的资源或者临时媒体文件。
如果你想在不外部存储保存一个公共文件,使用getExternalStroragePublicDirectory()方法来获取一个代表在外部文件的相应目录的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;
}
如果你想保存对你的应用程序私有的文件,你可以通过调用getExternalFileDir()方法获取相应的目录,并传递一个代表你想要的目录类型的名字。每个以这种方式被创建的目录是一个父目录,它代表了你的应用程序的素有内部存储文件的父目录,当用户卸载你的应用程序的时候系统删除。
例如,这里有一个方法,你能用于创建一个个人相片簿的目录:
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;
}
如果预定义的字母名称没有适合你的文件的,你可以调用getExternalFileDir()方法替代,传递null。这返回你的应用程序在外部存储的私有目录的根目录。
记住,getExternalFilesDir()方法创建一个目录,当用户卸载你的应用程序的时候系统删除目录中的文件。如果你保存的文件应该在用户卸载你的应用的时候被保存,例如当你的应用是一个照相机,并且用户想保存相片—你应该使用getExternalStoragePublicDirector()方法替代。
除非你使用getExternalStoragePublicDirectory()方法共享文件,或者getExternalFilesDir()方法对于你的应用私有的文件,你使用通过API常量如DIRECTORY_PICTURES提供的目录名称是重要的。这些目录名称确保文件被系统正确处理。例如,被保存在DIRECTORY_RINGTONES的文件通过系统媒体扫描器分类为铃声而不是音乐。
—————————————————————————————————————————————————————————————————
如果你提前知道你将要保存多少数据,你能找出是否有足够的空间有效,调用getFreeSpace()方法或者getTotalSapce()方法避免导致一个IOException。这些方法分别提供当前有效的空间和存储空间的总大小。这些信息也对于避免填充的存储空间超过一定的限值。
然而,系统不保证你能写和通过getFreeSpace()方法显示的一样多的字节。如果被返回的数字是几M的文件超过你想要保存的数据大小,或者如果文件系统少于90%,那么它可能是安全的进行。另一方面,你可能不能写存储。
注意:在你保存你的文件之前没有要求检查有效空间的大小。你能立刻尝试写文件,如果一个重现然后捕捉一个IOException。如果你不知道你确切需要多大空间,你可能需要这样做。例如,如果你在保存它之前通过转换为一个PNG图片为JPEG,改变文件的编码,你将无法提前知道文件的大小。
—————————————————————————————————————————————————————————————————
你总是应该删除你不在需要的文件。删除一个文件的最简单的方式是获取这个被打开的文件的引用,自己调用delete()方法。
myFile.delete();
如果这个文件被保存在一个内部存储,你也能通过deleteFile()方法请求Context来定位和删除一个文件:
myContext.deleteFile(fileName);
注意:当用户卸载你的应用程序的时候,Android系统删除以下:
所有你保存在内部存储的文件
所有你使用getExternalFilesDir()方法保存在外部存储的文件。
然而,你应该定期的手动删除所有使用getCacheDir()方法创建的缓存文件,和定期删除你不需要的文件。