Android文件存储

Android文件系统跟其他平台基于磁盘的文件系统类似,这篇教程描述了如何使用文件相关的API在Android文件系统上进行读写操作。

File对象适合读写大量的流式数据,如图片文件或其他文件的网络传输。

这篇教程将演示如何在App中执行基本的文件操作,并假设读者对Linux文件系统和Java标准的文件输入输出有一定基础。

选择内部存储还是外部存储

所有的Android设备都将文件存储区域分为两部分:内部存储和外部存储。这种命名来源于早期的Android系统,当时大部分的设备都有一个内置的不可变的内存(内部存储),另外还有一个可移除的存储介质,如SD卡(外部存储),后来即使设备没有可移除的存储介质了,依然习惯性地将永久存储空间划分为“内部”和“外部”,并且无论外部存储是否可以移除,这两部分存储空间的API行为都是一样的。下面分别总结每个存储空间的特点:

内部存储

1.总是可用的

2.内部存储空间内的文件默认只有你的App可以访问

3.当用户卸载了你的App,系统从内部存储空间中移除所有你的App相关的文件

当你希望用户和其他App都不能访问你的文件时,内部存储是最好的选择。

外部存储

1.并非总是可用的,因为用户可能将外部存储作为USB存储,某些情况下甚至会从设备上移除外部存储

2.它是大家都可以访问的,存储在这里的文件可以被其他应用程序访问

3.当用户卸载了你的App,系统仅仅会移除存储在通过 getExternalFilesDir()获取到的路径中的该App相关的文件

当你的文件不需要访问限制,或者你想将文件分享给其他的App,或者允许用户通过电脑来访问它,那么外部存储是最好的选择。。

小贴士

尽管App默认是被安装到内部存储空间中,但是你可以通过在AndroidManifest中指定android:installLocation属性来使App安装到外部存储空间。当APK文件很大且外部空间比内部空间大时,用户或许会喜欢这样做。如需要了解更多信息,请参考App InstallLocation

获取外部存储的权限

为了在外部存储空间中写入数据,需要在AndroidManifest文件中加入权限: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权限,那么它就默认有了读取外部存储空间的权限。

如果想保存文件到内部存储空间,则不需要声明任何权限。因为应用本身就有权限对内部存储空间进行读写。

保存文件到内部存储空间

当保存一个文件到内部存储空间时,可以通过下面任一个方法来获得一个合适的目录作为File:

getFilesDir()

为你的应用返回一个代表内部目录的File对象。

getCacheDir()

为你的应用返回一个代表内部临时缓存文件目录的File对象。需要对该文件目录设置一个合理的大小,如1M,并且确保在不需要的时候删除里面的文件。如果系统存储空间不足时,可能会在没有警告的条件下删除缓存的文件。

如果要在内部存储目录下创建一个文件,可以使用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;
}

注意:App的内部存储目录位于Android文件系统中通过应用包名决定的特定位置,从技术上讲,如果你的文件设置为可读,其他应用可以读取你的内部文件,但是,其他应用需要知道你应用的包名和文件名。如果你的文件没有设置为可读写,那么其他应用是不能访问该App的内部目录并读写你的文件的。因为只要使用了MODE_PRIVATE的模式,其他应用就不能访问。

保存文件到外部存储空间

因为外部存储空间有时是不可用的,比如将外部存储空间连接到PC或者SD卡被移除的时候,所以应该在访问它的时候检查它的可用性。通常可以使用getExternalStorgeState()来查询外部存储空间的状态,如果状态为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;
}

尽管外部存储可以被用户和其他应用访问修改,有两种类型的文件可以保存在这里:

Public files

这类文件对于用户或其他应用都是可访问的,当用户卸载了你的App时,这些文件依然保留并可被用户访问。例如,你的App所拍摄的照片或下载的文件。

Private files

这类文件仅属于你的App,并且会随着App的卸载而被删除。因为这些文件存储在外部,因此在技术上这些文件是可以被用户和其他应用访问的,但这对于你的App之外的用户是没有什么价值的。当用户卸载掉App时,系统会删除该App所属的外部私有目录下的所有文件。例如App下载的额外资源文件或临时性的多媒体文件。

如果想在外部存储空间上保存公有文件,可以使用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;
}

如果想保存对于App私有的文件,可以调用getExternalFilesDir()来获取合适的文件目录,并传入一个指定文件类型的参数。通过这种方式创建的目录都会被添加到封装了该App所有外部存储文件的目录下,并且会在用户卸载App时被系统删除。下面例子可以用来创建一个个人相册的目录:

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,它会返回外部存储空间上该App的私有目录的根目录。

请记住,通过getExternalFilesDir()方法创建的目录会随着App的卸载被删除。如果想在App被卸载后仍然可以使用这些文件,比如一个拍照的App,我们想在卸载后依然保存照片,那么就应该使用getExternalStoragePublicDirectory()。

无论是使用getExternalStoragePublicDirectory()来保存共享文件还是使用getExternalFilesDir()保存App私有的文件,重要的是尽可能使用API常量提供的目录名称,如DIRECTORY_PICTURES,这些目录名称能保证系统正确地对待里面的文件。例如,以DIRECTORY_RINGTONES类型存储的文件会被系统识别为铃声而不是音乐。

查询剩余空间

如果你事先知道要保存的文件的大小,可以通过调用getFreeSpace()或getTotalSpace()来判断是否有足够的空间,从而避免发生IOException。这些方法可以获得当前可用的空间以及总容量的大小,这些信息对于在一定阈值以上的存储空间上存储文件是很有用的。

然而,系统并不能保证可以写入getFreeSpace()大小的文件,如果剩余空间比你想要存储的文件大几M,或者存储空间使用还不到90%,那么继续存储文件一般是安全的,否则最好不要再写入文件。

注意:其实并非强制在保存文件之前一定要对剩余空间进行查询。你可以直接尝试写入文件,然后捕捉IOException,如果你事先不知道到底需要多少存储空间的时候可以这样做。例如,当你把PNG图片转换为JPEG时,事先并不知道生成的图片大小是多少。

删除文件

你应该在不再需要的时候删除文件,最直接的方法是通过调用文件的delete()方法。

myFile.delete();

如果文件是保存在内部存储空间上的,还可以调用Context的deleteFile()方法来定位和删除文件。

myContext.deleteFile(fileName);

注意:当用户卸载了App,Android系统会删除如下文件:

1.所有保存在内部存储空间的文件

2.所有保存在通过getExternalFilesDir()获取的外部存储空间目录的文件

然而,你应该定期地删除所有通过getCacheDir()创建的缓存文件以及那些不再使用的文件。

原文地址:http://developer.android.com/training/basics/data-storage/files.html

    原文作者:aspook
    原文地址: https://blog.csdn.net/u014738140/article/details/47659263
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞