保存文件
内部存储和外部存储
内部存储
- 始终可用
- 保存文件只能被本App访问
- 卸载应用,系统删除内部存储中你的app程序的所有文件
如果您想确保用户和其他应用程序都无法访问您的文件,则最好使用内部存储
外置存储(External storage):
- 并不总是可用的,因为用户可以选择把这部分作为USB存储模式,这样就不可以访问了。
- 是大家都可以访问的,因此保存到这里的文件是失去访问控制权限的。
- 当用户卸载你的app时,系统仅仅会删除external根目录( getExternalFilesDir() )下的相关文件。
External是在你不需要严格的访问权限并且你希望这些文件能够被其他app所共享或者是允许用户通过电脑访问时的最佳存储区域。
保存内部存储:
- File getFilesDir()
返回File表示跟应用程序关联的目录app内部目录,/data/data/youPackageName/files的File对象
。
- File getCacheDir()
返回File表示文件系统上与您的应用唯一关联的缓存目录,此目录适用于临时文件,应定期清理。如果磁盘空间不足,系统可能会删除那里的文件,因此请确保在读取之前检查缓存文件是否存在,返回 /data/data/youPackageName/cache的File对象
警告:
File getCacheDir()
如果系统存储空间不足,则可能会在没有警告的情况下删除缓存文件。
例如:
操作 getFilesDir()
File file = new File(context.getFilesDir(), filename);
或者
调用openFileOutput()
来获取写入内部目录中文件的FileOutputStream
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();
}
FileOutputStream openFileOutput(String name, int mode)
参数:
name 要打开的文件名,不能包含路径分割符
mode 操作模式,值:0 或
MODE_PRIVATE
默认模式,其中创建的文件只能由调用应用程序(或共享相同用户ID的所有应用程序)访问。
MODE_WORLD_READABLE
非常危险,已经禁止使用了。
MODE_WORLD_WRITEABLE
非常危险,已经禁止使用了。
如果您的应用程序需要与其他应用共享的私人文件,你应该使用一个FileProvider用FLAG_GRANT_READ_URI_PERMISSION。参阅共享文件。
MODE_APPEND
文件存在,数据在文件末尾追加,而不是擦除
操作 getCacheDir()
如果您需要缓存某些文件,则应使用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()
创建的文件放在app专用的缓存目录中。您应该定期删除不再需要的文件。
警告: 如果系统存储空间不足,可能会在没有警告的情况下删除缓存文件,因此请确保在读取之前检查缓存文件是否存在。
打开文件
openFileInput(name) 传递文件名,打开一个文件
fileList() 返回一个app中所有文件名的数组
提示:如果您需要在应用程序中打包一个可在安装时访问的文件,请将该文件保存在项目的res/raw/目录中。您可以使用openRawResource()传递资源ID 打开这些文件。此方法返回可用于读取文件的方法。您无法写入原始文件。 R.raw.filename InputStream
getDir(String name, int mode)
,返回/data/data/youPackageName/
下的指定名称的文件夹File对象,如果该文件夹不存在则用指定名称创建一个新的文件夹。
文件保存在外部存储
保存两种不同类型的文件:
公共文件:应该可供其他应用程序和用户免费使用的文件。当用户卸载您的应用时,这些文件应该仍然可供用户使用。例如,应用程序或其他下载文件捕获的照片应保存为公共文件。
私人文件:应用程序文件,用户卸载应用时删除。虽然这些文件在技术上可由用户和其他应用程序访问,因为它们位于外部存储上,但它们不会为应用程序外的用户提供价值。
警告:如果用户卸下SD卡或将设备连接到计算机,外部存储可能会变得不可用。并且用户和具有该READ_EXTERNAL_STORAGE 权限的其他应用程序仍然可以看到这些文件。因此,如果您的应用程序的功能取决于这些文件,或者您需要完全限制访问,则应将文件写入内部存储。
1.请求外部存储权限
在 manifest 请求 WRITE_EXTERNAL_STORAGE
权限
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
如果您的应用使用
WRITE_EXTERNAL_STORAGE
权限,则它也隐式拥有读取外部存储的权限。
如果您的应用只需要读取,不写入文件,则可以申请READ_EXTERNAL_STORAGE
<manifest ...>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
...
</manifest>
警告:4.4以下 低版本外部存储见文档。
验证外部存储是否可用
由于外部存储可能不可用 - 例如当用户将存储装置安装到PC或已移除提供外部存储的SD卡时 - 您应始终在访问之前验证该卷是否可用。您可以通过调用查询外部存储状态getExternalStorageState()
。如果返回状态为MEDIA_MOUNTED
,则可以读取和写入文件。如果是MEDIA_MOUNTED_READ_ONLY
,则只能读取文件。
例如,以下方法可用于确定存储可用性:
/* 检查外部存储器是否可用于读写 */
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
/* 检查外部存储是否可用于读取 */
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) {
// 获取用户公共图片的目录
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) {
// 获取应用程序的私有图片目录
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()
还是 应用程序专用的文件 getExternalFilesDir()
,使用API常量提供的目录名称都很重要 DIRECTORY_PICTURES
。这些目录名称可确保系统正确处理文件。例如,保存在的文件 DIRECTORY_RINGTONES
被系统媒体扫描仪分类为铃声而不是音乐。
在多个存储位置之间选择
有时,分配内部存储器分区以用作外部存储器的设备也提供SD卡插槽。这意味着该设备有两个不同的外部存储目录,因此您需要选择在将“私有”文件写入外部存储时使用哪个目录。
从Android 4.4(API级别19)开始,您可以通过调用访问这两个位置getExternalFilesDirs()
,该位置 返回一个File
包含每个存储位置条目的数组。阵列中的第一个条目被视为主要外部存储,您应该使用该位置,除非它已满或不可用。
如果您的应用支持Android 4.3及更低版本,则应使用支持库的静态方法ContextCompat.getExternalFilesDirs()
。这总是返回一个File
数组,但是如果设备运行的是Android 4.3及更低版本,那么它只包含一个主外部存储条目(如果有第二个存储位置,则无法在Android 4.3及更低版本上访问它)。
查询可用空间
如果您提前知道要保存多少数据,则可以查看是否有足够的空间可用而不会导致IOException
通过呼叫 getFreeSpace()
或getTotalSpace()
。这些方法分别提供当前可用空间和存储卷中的总空间。此信息对于避免将存储卷填充到某个阈值以上也很有用。
但是,系统不保证您可以写入指示的字节数getFreeSpace()
。如果返回的数字比您要保存的数据大小多几MB,或者文件系统小于90%已满,则可以继续。否则,您可能不应该写入存储。
注意: 在保存文件之前,如果您不确切知道需要多少空间,则可能需要执行此操作, 无需检查可用空间量。您可以尝试立即编写文件,然后捕获一个IOException是否发生。例如,如果在通过将PNG图像转换为JPEG来保存之前更改文件的编码,则事先不会知道文件的大小。
删除文件
删除文件最直接的方法就是调用File
对象 delete()
方法
myFile.delete()
如果文件保存在内部存储器中,您还可以Context
通过调用deleteFile()
以下命令来查找和删除文件
myContext.deleteFile(fileName);
注意:当用户卸载您的应用时,Android系统会删除以下内容:
- 您在内部存储上保存的所有文件。
- 使用保存外部存储的所有文件getExternalFilesDir()
。
但是,您应手动删除getCacheDir()
定期创建的所有缓存文件, 并定期删除不再需要的其他文件。