Android 文件操作
概述
Android 中的文件操作主要涉及到两个部分,一个是内部存储的读写,一个是外部存储的读写两者的主要区别如下表:
内部存储 | 外部存储 |
---|---|
始终可用 | 它并非始终可用,因为用户可采用 USB 存储的形式装载外部存储,并在某些情况下会从设备中将其删除。 |
默认情况下只有您的应用可以访问此处保存的文件。 | 它是全局可读的,因此此处保存的文件可能不受您控制地被读取 |
当用户卸载您的应用时,系统会从内部存储中删除您的应用的所有文件。 | 当用户卸载您的应用时,只有在您通过 getExternalFilesDir() 将您的应用的文件保存在目录中时,系统才会从此处删除您的应用的文件。 |
当您希望确保用户或其他应用均无法访问您的文件时,内部存储是最佳选择 | 对于无需访问限制以及您希望与其他应用共享或允许用户使用电脑访问的文件,外部存储是最佳位置。 |
内部存储
内部文件
你可以直接使用内部存储,而无需任何的权限。默认情况下,内部存储的文件只属于你的应用,任何其他应用都无法访问。并且内部存储的文件在应用被卸载的时候会被删除
FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(string.getBytes());
fos.close();
或者
File file = new File(getFilesDir(), "hello_file2");
if(!file.exists()){
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
fos.write(string.getBytes());
fos.close();
这里一般使用Context.MODE_PRIVATE就可以了。你还以使用MODE_WORLD_READABLE 和 MODE_WORLD_WRITEABLE. 这样你的文件就可以被其他应用访问到。但是这两个选项已经被废弃了。并且在未来的android N 中如果使用, 会抛出 SecurityException, 如果你真的要与其他应用之间共享文件,请使用FileProvider.
当你需要读取内部存储的文件的时候,你可以使用:
openFileInput(String fileName)
获取一个FileInputStream.. 或者使用
File file = new File(getFileFir(), fileName)
如果你想在应用编译的时候就带有一些文件, 你可以选择存放在 assets/ 目录, 你可以通过:
InputStream is = getResources().getAssets().open(String fileName);
来读取该文件。
或者你也可以将文件放在 res/raw/ 目录下通过:
InputStream is = getResources().openRawResource(R.raw.raw_file);
来获取该文件的InputStream.
内部缓存 Cache
内部存储中与file 文件夹同级有一个cache 文件夹。可以用来存储一些缓存文件, 你可以通过:
File file = new File(getCacheDir(), fileName)
来创建文件。
但是缓存文件并不能保证持久。 系统内存低的时候,系统会优先删除这些缓存文件。如果你写入一些缓存文件, 你需要自己管理这个cache 的大小以及文件的存活时间。
外部存储
外部存储通常所指为我们的sdcard 存储。但是现在大部分出厂的手机都会在内部存储器上划出一部分作为外部存储sdcard。当然还是有很多手机也支持外部 sdcard 扩展的。如果手机上插有外部的sdcard. 我们的手机Settings里面一般会有一个选项可以选择哪一个作为当前的主存储。
由于外部存储存在着在PC上挂载的可能性,所以每次使用之前需要对外存储的mount state 做检查
/* 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;
}
保证外部存储是可读写的状态才可以继续操作。
私有文件
android 在外部存储上会为每一个app 建立私有文件夹 Android/data/应用包名/ 下 当然这里的文件夹虽然是私有,但是对其他人都是可读可写的。如果你想要文件只被自己读写,还是建议存储在内部存储。
私有文件夹里的内容与内部存储的文件一样,在应用删除之后也会被清除。
对于外部存储的私有文件的读写,android4.4及以后并不需要声明特别的权限。但是对于4.4以前需要申明WRITE_EXTERNAL_STORAGE 权限.
或者你也可以这样做:
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
...
</manifest>
想要获取外部存储的私有文件夹, 你可以通过:
File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), FILENAME);
来获取这个文件夹的File 对象。 type可以是
DIRECTORY_PICTURES 之类的, 会返回对应的Pictures 文件夹。或者直接传null会直接返回这个目录的文件夹。
公有文件
公有文件存储必须要声明WRITE_EXTERNAL_STORAGE 权限.
公有文件不属于app 所有。当你的应用被删除的时候应该被保留的文件,如用户的相片,铃声等。
想要获取外部存储的公有文件夹, 你可以通过:
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), FILENAME);
type 也可以是DIRECTORY_MUSIC
, DIRECTORY_PICTURES
,DIRECTORY_RINGTONES
存储在这里文件夹下面的文件,会被系统media scanner 扫面到。由media provider 提供给用户使用。
在读写外部存储公有文件的时候,在android 6.0 之后android 权限系统由安装时改成运行时检查。所以如果你的应用目标SDK版本(targetSDKVersion)为23及以上时(这意味着程序可以在Android 6.0及以上的版本中运行),将应用安装在Android 6.0及以上机型中时,需要动态的申请权限:
//申请权限
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
int hasWriteSdcardPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if(hasWriteSdcardPermission != PackageManager.PERMISSION_GRANTED){
requestPermissions(new String[]{"android.permission.WRITE_EXTERNAL_STORAGE"} ,
REQUEST_WRITE_EXTNARL_PERMISSION);
return;
}
}
writeSdcard();
获取权限后的回调:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if(requestCode == REQUEST_WRITE_EXTNARL_PERMISSION){
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission Granted
writeSdcard();
} else {
// Permission Denied
Toast.makeText(MainActivity.this, "WRITE_CONTACTS Denied", Toast.LENGTH_SHORT)
.show();
}
}else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
外置sdcard 的 操作:
如果你的手机带有外置sdcard, 你想对外置的sdcard 进行读写操作,从Android4.4开始你可以用 getExternalFilesDirs()来访问这两个地方, 这个方法会返回一个File 数组,这里会返回2个file,file[0] primary external storage ,file[1]为 second external storage, 你也可以使用[ContextCompat.getExternalFilesDirs()](https://developer.android.com/reference/android/support/v4/content/ContextCompat.html#getExternalFilesDirs(android.content.Context, java.lang.String)), 但是android4.3 及以前的手机,只会返回一个值为当前的主存储的File。
.
File[] files = ContextCompat.getExternalFilesDirs(this,null);
//当一个手机插入sdcard之后。这里会返回2个file,file[0] primary external storage ,file[1]为 second external storage,
// file[0] 与 file 相同
if(files.length > 1) {
if (EnvironmentCompat.getStorageState(files[1]).equals(Environment.MEDIA_MOUNTED)){
try {
String FILENAME = "pic_file.png";
File file = new File(files[1], FILENAME);
当然,在读取之前也需要做StorageState 的检查。
本文介绍了android 中文件操作的方法,以及使用过程中的注意的问题。更多的代码你可以查看示例:
示例Demo
参考资料:
https://developer.android.com/training/basics/data-storage/files.html#GetWritePermission
https://developer.android.com/guide/topics/data/data-storage.html#AccessingExtFiles
http://jijiaxin89.com/2015/08/30/Android-s-Runtime-Permission/