Android 文件保存

选择内部或外部存储

全部Android设备都有两个文件存储区:“内部”和“外部”存储。这些名称来自Android的早期,大多数设备提供内置的非易失性存储器(内部存储),以及可移动存储介质,如microSD卡(外部存储)。如今,许多设备将永久存储空间划分为单独的“内部”和“外部”分区。所以,即便没有可移动存储介质,这两个存储空间也始终存在,不管外部存储是否可移动,API行为都是相同的。android

因为外部存储多是可移除的,所以这两个选项之间存在一些差别,以下所示。数组

内部存储器:

  • 它始终可用。
  • 内部存储器保存的文件只能由您的应用访问。
  • 当用户卸载您的应用程序时,系统会从内部存储中删除全部应用程序的文件。
  • 当您但愿确保用户和其余应用程序都没法访问您的文件时,最好使用内部存储。

外部存储:

  • 外部存储并不老是可用,由于用户能够将外部存储装载为USB存储器,并在某些状况下将其从设备中移除。
  • 外部存储是全局可读的,所以保存在此处的文件可能会在您的控件以外读取。
  • 当用户卸载您的应用程序时,只有将应用程序的文件保存在经过使用getExternalFilesDir()获取的目录时,系统才会今后处删除应用程序的文件。
  • 对于不须要访问限制的文件以及要与其余应用程序共享或容许用户使用计算机访问的文件,外部存储是最佳位置。

将文件保存在内部存储上

您的应用程序的内部存储目录由您的应用程序包名称指定在Android文件系统的特殊位置,可使用如下API访问。缓存

注意:与外部存储目录不一样,您的应用程序不须要任何系统权限来读取和写入这些方法返回的内部目录。bash

写一个文件

将文件保存到内部存储时,能够经过调用如下两种方法获取相应的目录,进一步操做Fileapp

  • getFilesDir():返回File表示应用程序的内部目录。
  • getCacheDir():返回File表示应用程序临时缓存文件的内部目录。

要在其中一个目录中建立新文件,可使用File()构造函数,传递给File上述方法提供的指定内部存储目录的方法。ide

例如:函数

File file = new File(context.getFilesDir(), filename);
复制代码

或者,您能够调用openFileOutput()以获取FileOutputStream 写入内部目录中的文件的内容。ui

例如,如下是如何将一些文本写入文件:url

String filename = "myfile";
String fileContents = "Hello world!";
FileOutputStream outputStream;

try {
    outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
    outputStream.write(fileContents.getBytes());
    outputStream.close();
} catch (Exception e) {
    e.printStackTrace();
}
复制代码

MODE_PRIVATE 将会建立文件(或替换具备相同名称的文件),并将其设为应用的私有文件。 其余可用模式包括:MODE_APPENDMODE_WORLD_READABLEMODE_WORLD_WRITEABLEspa

请注意,该openFileOutput()方法须要文件模式参数。传递 MODE_PRIVATE 将会建立文件(或替换具备相同名称的文件),并将其设为应用的私有文件。 其余可用模式包括:MODE_APPENDMODE_WORLD_READABLEMODE_WORLD_WRITEABLE

自 API 级别 17 以来,常量 MODE_WORLD_READABLEMODE_WORLD_WRITEABLE 已被弃用。从 Android N(7.0,API24) 开始,使用这些常量将会致使引起 SecurityException。这意味着,面向 Android N 和更高版本的应用没法按名称共享私有文件,尝试共享“file://”URI 将会致使引起 FileUriExposedException。 若是您的应用须要与其余应用共享私有文件,则能够将 FileProviderFLAG_GRANT_READ_URI_PERMISSION配合使用

在Android 6.0(API级别23)及更低级别上,若是您将文件模式设置为全局可读,则其余应用程序能够读取您的内部文件。可是,其余应用必须知道您的应用包名称和文件名。除非您明确将文件设置为可读或可写,不然其余应用程序没法浏览您的内部目录而且没有读取或写入权限·所以,只要您在内部存储上使用MODE_PRIVATE标记您的文件,其余应用就永远没法访问它们

写一个缓存文件

若是你须要缓存一些文件,你应该使用 createTempFile()

例如,如下方法从URL中提取文件名,并在应用程序的内部缓存目录中建立具备该名称的文件:

private 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;
}
复制代码

使用createTempFile()建立的文件放置在应用程序专用的缓存目录中。您应该按期删除再也不须要的文件。

注意: 若是系统存储空间不足,可能会在没有警告的状况下删除缓存文件,所以请确保在读取以前检查缓存文件是否存在。

打开现有文件

要读取现有文件,请调用openFileInput(name),传递文件名。

您能够经过调用获取全部应用程序文件名的数组 fileList()

注意:若是您须要在应用程序中打包一个可在安装时访问的文件,请将该文件保存在项目的res/raw/目录中。您可使用openRawResource()传递资源ID 打开这些文件。此方法返回可用于读取文件的方法。您没法写入原始文件。

打开一个目录

您可使用如下方法在内部文件系统上打开目录:

  • getFilesDir():返回File表示文件系统上与您的应用惟一关联的目录。
  • getDir(name, mode):在应用程序的惟一文件系统目录中建立新目录(或打开现有目录)。此新目录显示在提供的目录中getFilesDir()
  • getCacheDir():返回File表示文件系统上与您的应用惟一关联的缓存目录。此目录适用于临时文件,应按期清理。若是磁盘空间不足,系统可能会删除那里的文件,所以请确保在读取以前检查缓存文件是否存在。

要在其中一个目录中建立新文件,可使用 File()构造函数,传递File上述方法之一提供的指定内部存储目录的对象。

例如:

File directory = context.getFilesDir();
File file = new File(directory, filename);
复制代码

将文件保存在外部存储上

使用外部存储很是适合您要与其余应用共享容许用户使用计算机访问的文件

请求存储权限验证存储可用后,您能够保存两种不一样类型的文件:

  • 公共文件:应该可供其余应用程序和用户无偿使用的文件。当用户卸载您的应用时,这些文件应该仍然可供用户使用。例如,应用程序或其余下载文件捕获的照片应保存为公共文件。
  • 私人文件:合法属于您的应用的文件,将在用户卸载您的应用时删除。虽然这些文件在技术上可由用户和其余应用程序访问,由于它们位于外部存储上,但它们不会为应用程序外的用户提供价值。

注意若是用户卸下SD卡或将设备链接到计算机,外部存储可能会变得不可用。而且用户和具备READ_EXTERNAL_STORAGE 权限的其余应用程序仍然能够看到这些文件。所以,若是您的应用程序的功能取决于这些文件,或者您须要彻底限制访问,则应将文件写入内部存储。

请求外部存储权限

要写入公共外部存储,您必须在清单文件中请求WRITE_EXTERNAL_STORAGE权限:

<uses-permission android:name = “android.permission.WRITE_EXTERNAL_STORAGE” />  
复制代码

若是您的应用使用该WRITE_EXTERNAL_STORAGE权限,则它也隐式拥有读取外部存储的权限。

若是您的应用只须要读取外部存储(但不能写入),那么您须要声明 READ_EXTERNAL_STORAGE权限:

<uses-permission android:name = “android.permission.READ_EXTERNAL_STORAGE” />
复制代码

从Android 4.4(API级别19)开始,在应用程序的私有外部存储目录中读取或写入文件 - 使用getExternalFilesDir() 访问, 不须要READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE 权限。所以,若是您的应用支持Android 4.3(API级别18)及更低版本,而且您只想访问专用外部存储目录,则应经过添加maxSdkVersion 属性声明仅在较低版本的Android上请求权限 :

<uses-permission android:name = “android.permission.WRITE_EXTERNAL_STORAGE”
android:maxSdkVersion = “18” /> 
复制代码

验证外部存储是否可用

因为外部存储可能不可用 - 例如当用户将存储装置安装到PC或已移除提供外部存储的SD卡时 - 您应始终在访问以前验证该卷是否可用。您能够经过调用来查询外部存储状态getExternalStorageState()。若是返回状态为MEDIA_MOUNTED,则能够读取和写入文件。若是是MEDIA_MOUNTED_READ_ONLY,则只能读取文件。

例如,如下方法可用于肯定存储可用性:

/* 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 getPublicAlbumStorageDir(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; } 复制代码

若是要从Media Scanner中隐藏文件,请在外部文件目录中包含一个名为.nomedia空文件(请注意文件名 中的点前缀)。这能够防止媒体扫描程序读取您的媒体文件,并经过MediaStore内容提供商将其提供给其余应用程序。

保存到外部私有目录

若是要将文件保存在应用程序专用且外部提供程序没法访问的外部存储上MediaStore,您能够经过调用getExternalFilesDir()并向其传递一个名称来获取一个目录,该目录仅由您的应用程序使用, 该名称指示您但愿的目录类型。以这种方式建立的每一个目录都会添加到父目录中,该目录封装了应用程序的全部外部存储文件,系统会在用户卸载应用程序时将其删除。

public File getPrivateAlbumStorageDir(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() 建立一个在用户卸载应用程序时删除的目录。若是您保存的文件在用户卸载应用程序后仍然可用 - 例如当您的应用程序捕获照片而且用户应保留这些照片时 - 您应该将文件保存到公共目录。

在多个存储位置之间选择

有时,分配内部存储器分区以用做外部存储器的设备也提供SD卡插槽。这意味着该设备有两个不一样的外部存储目录,所以您须要选择在将“私有”文件写入外部存储时使用哪一个目录。

从Android 4.4(API级别19)开始,您能够经过调用访问这两个位置getExternalFilesDirs(),该位置 返回一个包含每一个存储位置条目的File数组。数组中的第一个条目被视为主要外部存储,您应该使用该位置,除非它已满或不可用。

若是您的应用支持Android 4.3及更低版本,则应使用支持库的静态方法ContextCompat.getExternalFilesDirs()。这老是返回一个File数组,但若是设备运行的是Android 4.3及更低版本,那么它只包含一个主外部存储条目(若是有第二个存储位置,则没法在Android 4.3及更低版本上访问它)。

删除文件

您应该始终删除您的应用再也不须要的文件。删除文件最直接的方法是调用File对象delete()方法。

myFile.delete();
复制代码

若是文件保存在内部存储器上,您还能够经过调用ContextdeleteFile()来查找和删除文件:

myContext.deleteFile(fileName );
复制代码

注意:当用户卸载您的应用时,Android系统会删除如下内容:

  • 您在内部存储上保存的全部文件。
  • 使用getExternalFilesDir()保存在外部存储的全部文件
  • 可是,您应该手动删除getCacheDir()按期建立的全部缓存文件, 并按期删除再也不须要的其余文件。

Android 目录总结

($rootDir)
+- /data                -> Environment.getDataDirectory()
|   |
|   |   ($appDataDir)
|   +- data/com.srain.cube.sample
|       |
|       |   ($filesDir)
|       +- files            -> Context.getFilesDir() / Context.getFileStreamPath("")
|       |       |
|       |       +- file1    -> Context.getFileStreamPath("file1")
|       |   ($cacheDir)
|       +- cache            -> Context.getCacheDir()
|       |
|       +- app_$name        ->(Context.getDir(String name, int mode)
|
|   ($rootDir)
+- /storage/sdcard0     -> Environment.getExternalStorageDirectory()
    |                       / Environment.getExternalStoragePublicDirectory("")
    |
    +- dir1             -> Environment.getExternalStoragePublicDirectory("dir1")
    |
    |   ($appDataDir)
    +- Andorid/data/com.srain.cube.sample
        |
        |   ($filesDir)
        +- files        -> Context.getExternalFilesDir("")
        |   |
        |   +- file1    -> Context.getExternalFilesDir("file1")
        |   +- Music    -> Context.getExternalFilesDir(Environment.Music);
        |   +- Picture  -> ... Environment.Picture
        |   +- ...
        |
        |   ($cacheDir)
        +- cache        -> Context.getExternalCacheDir()
        |
        +- ???
复制代码
相关文章
相关标签/搜索