获取访问外部存储的权限
为了在外部存储空间当中对文件进行操作,需要获取READ_EXTERNAL_STORAGE或者WRITE_EXTERNAL_STORAGE,如果你同时需要读写文件,那么只需要声明WRITE_EXTERNAL_STORAGE权限即可,因为这个权限默认包括读权限。从Android4.4开始,如果操作的文件是APP私有的,就不需要声明权限。
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
查询外部存储是否可获取
因为外部存储空间可能是不可获取的,比如当用户把外部存储空间挂载到PC或者已经移除了SD卡,所以在使用之前得确认是否能够获取到外部存储空间,代码如下所示:
/* 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;
}
外部存储中的文件是可以被用户或者其他应用程序修改的,有两种类型的文件(或者目录):
公共文件Publicfiles:
文件是可以被自由访问,且文件的数据对其他应用或者用户来说都是有意义的,当应用被卸载之后,其卸载前创建的文件仍然保留。比如camera应用,生成的照片大家都能访问,而且camera不在了,照片仍然存在。如果你想在外存储上放公共文件你可以使用getExternalStoragePublicDirectory(),通过传入你想要的目录类型,这些类型应该是如下类型中的一种:DIRECTORY_MUSIC, DIRECTORY_PODCASTS, DIRECTORY_RINGTONES, DIRECTORY_ALARMS, DIRECTORY_NOTIFICATIONS, DIRECTORY_PICTURES, DIRECTORY_MOVIES, DIRECTORY_DOWNLOADS, or DIRECTORY_DCIM. 或者也可以为空。使用这些API常量,系统的media scanner就可以正确的对你的文件进行归类。比如,保存在DIRECTORY_RINGTONES目录下的文件会被系统的媒体扫描器归类为铃声而不是音乐,当然你也可以指定你自己的常量。
示例代码如下,来自samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java:
void createExternalStoragePublicPicture() {
// Create a path where we will place our picture in the user's
// public pictures directory. Note that you should be careful about
// what you place here, since the user often manages these files. For
// pictures and other media owned by the application, consider
// Context.getExternalMediaDir().
File path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
File file = new File(path, "DemoPicture.jpg");
try {
// Make sure the Pictures directory exists.
path.mkdirs();
// Very simple code to copy a picture from the application's
// resource into the external file. Note that this code does
// no error checking, and assumes the picture is small (does not
// try to copy it in chunks). Note that if external storage is
// not currently mounted this will silently fail.
InputStream is = getResources().openRawResource(R.drawable.balloons);
OutputStream os = new FileOutputStream(file);
byte[] data = new byte[is.available()];
is.read(data);
os.write(data);
is.close();
os.close();
// Tell the media scanner about the new file so that it is
// immediately available to the user.
MediaScannerConnection.scanFile(this,
new String[] { file.toString() }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
}
});
} catch (IOException e) {
// Unable to create file, likely because external storage is
// not currently mounted.
Log.w("ExternalStorage", "Error writing " + file, e);
}
}
如果你的api 版本低于8,那么不能使用getExternalStoragePublicDirectory(),而是使用Environment
.getExternalStorageDirectory(),他不带参数,也就不能自己创建一个目录,只是返回外部存储的根路径(当然现在还有很多应用程序直接使用getExternalStorageDirectory()得到根目录,然后在根目录下建立自己的目录和文件)。
私有文件Private files:
其实由于是外部存储的原因,即使是这种类型的文件也能被其他程序访问,只不过一个应用私有的文件对其他应用其实是没有访问价值的(恶意程序除外)。外部存储上,应用私有文件的价值在于卸载之后,这些文件也会被删除。类似于内部存储。通过使用getExternalFilesDir()函数,就可以获取到私有的存储目录。尽管这里面的文件不会自动被media scanner扫描,我们可以手动把他们加入media database,通过使用MediaScannerConnection.scanFile方法。
代码如下,来自samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java:
void createExternalStoragePrivatePicture() {
// Create a path where we will place our picture in our own private
// pictures directory. Note that we don't really need to place a
// picture in DIRECTORY_PICTURES, since the media scanner will see
// all media in these directories; this may be useful with other
// media types such as DIRECTORY_MUSIC however to help it classify
// your media for display to the user.
File path = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File file = new File(path, "DemoPicture.jpg");
try {
// Very simple code to copy a picture from the application's
// resource into the external file. Note that this code does
// no error checking, and assumes the picture is small (does not
// try to copy it in chunks). Note that if external storage is
// not currently mounted this will silently fail.
InputStream is = getResources().openRawResource(R.drawable.balloons);
OutputStream os = new FileOutputStream(file);
byte[] data = new byte[is.available()];
is.read(data);
os.write(data);
is.close();
os.close();
// Tell the media scanner about the new file so that it is
// immediately available to the user.
MediaScannerConnection.scanFile(this,
new String[] { file.toString() }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
}
});
} catch (IOException e) {
// Unable to create file, likely because external storage is
// not currently mounted.
Log.w("ExternalStorage", "Error writing " + file, e);
}
}
所有应用程序的外部存储的私有文件都放在根目录的Android/data/下,目录形式为/Android/data/<package_name>/
如果你的api 版本低于8,那么不能使用getExternalFilesDir()
,而是使用Environment
.getExternalStorageDirectory()获得根路径之后,自己再想办法操作/Android/data/<package_name>/下的文件。
也就是说api 8以下的版本在操作文件的时候没有专门为私有文件和公共文件的操作提供api支持。你只能先获取根目录,然后自行想办法。
最后为了弄清楚getExternalFilesDir,getExternalStorageDirectory,getExternalStoragePublicDirectory等android文件操作方法,我将这些方法的执行结果打印出来,看看到底路径是啥样,在activity中执行以下代码:
// 打印各种方法获取到的绝对路径
Log.i(TAG, "getFilesDir = " + getFilesDir());
Log.i(TAG, "getExternalFilesDir = "
+ getExternalFilesDir("exter_test").getAbsolutePath());
Log.i(TAG, "getDownloadCacheDirectory = "
+ Environment.getDownloadCacheDirectory().getAbsolutePath());
Log.i(TAG, "getExternalCacheDirectory = "
+ getExternalCacheDir().getAbsolutePath());
Log.i(TAG, "getDataDirectory = "
+ Environment.getDataDirectory().getAbsolutePath());
Log.i(TAG, "getExternalStorageDirectory = "
+ Environment.getExternalStorageDirectory().getAbsolutePath());
Log.i(TAG,
"getExternalStoragePublicDirectory = "
+ Environment
.getExternalStoragePublicDirectory("pub_test"));
打印结果如下: