android所使用的文件系统类似与其他系统的那种基础存储盘的文件系统。这一节主要说一下android文件系统如何利用 File 这个API来读写文件。
File 对象适用于按照从头到尾的顺序读写大的数据,中间不会有部分内容的跳过操作。比方说,这个就很适用于图片文件或者是通过网络传输的文件的保存和读取。
这一节里面说一下在你的APP里面如何进行和文件相关的基本操作。这个课程假设你对linux文件系统和
java.io
里面的输入\输入API有一定的熟悉.
谷歌官方:点击打开链接
选择外部或者内部存储器
android设备有2个存储区域:“internal”(外部)和“external”(内部)存储器。这个命名来自于早期的android,当时android提供一个内建的固态存储器(internal),外面可以加一个多媒体存储器,例如micro SD卡(external).有的设备会把它的固态存储器,分为2个部分,一个当做内部存储器,一个部分当做外部存储器,所以,即使外面没有一个可移除的SD卡,这个设备也还是有2个存储区域,当我们用系统的相关函数去获取外部和外部的存储路径的时候,不管外部存储器移走还是没有,结果都是一样的。
下面是对每个存储区域的一些实际总结。
内部存储器:
总是可用的
保存在这里的文件只对这个文件的所有者有权限访问操作
当用户卸载掉APP的时候,这个APP存储在内部存储器的文件将会全部被删除
内部存储器是你存放一些不希望别的用户或者APP访问的文件的最好位置
外部存储器:
不是总是可用的,因为用户有时候会把它当做一个USB存储器或者是移除掉它
是通用可读的,所以放在这里的文件,可用被其他的APP读取,无法控制
当用户卸载APP的时候,仅仅是会自动删除存储在
getExternalFilesDir()方法返回的路径下的文件
外部存储器适合存放那些不敏感的,不需要限制的以及那些希望别的APP也能访问到的数据
提示:尽管APP默认是安装在内部存储器,但是你也可以利用
android:installLocation
在manifest里面指定你的APP安装到外部存储器。当用户在有一个比内部存储器大很多的外部存储器的时候,如果安装一个很大的APK包,那么就会很在意这一点。想了解更多,请阅读:App Install Location.
获取外部存储器的权限
如果想往外部存储器写数据,APP必须要在manifest文件里面声明
WRITE_EXTERNAL_STORAGE权限
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
警告:当前所有的APP在没有声明特殊权限下情况下,读取外部存储器内容的能力。这个问题在后面发布的版本会修正。如果APP需要仅仅从外部存储器读取内容,需要声明READ_EXTERNAL_STORAGE 权限。为了保证APP在将来的版本上也正常运行,现在就最好加上这个属性的声明。
<manifest ...>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
...
</manifest>
不管如何,如果APP声明了WRITE_EXTERNAL_STORAGE这个写权限,也就意味着有了读权限,虽然没有明确的指明。
保存文件到内部存储器上不需要任何的权限。APP一直都用在内部存储器上读写的权限。
保存文件到内部存储器
如果要保存文件到内部存储器,首先要获取一个合适的路径,可以调用下面2个函数中的一个来获取,返回的是一个包含有路径的
File对象
getFilesDir() 返回File对象代表着你的APP可以使用的内部存储路径
getCacheDir() 返回的File对象代表着你的APP的可以使用的临时缓存文件的路径。在不需要的这个文件的时候,要保证删除掉它,并且在创建的时候,限制一个合适的大小,比方说1M。如果系统运行时,存储空间很小的时候,它可能会直接删除缓存问题,并且不会给出任何警告。
当需要在上面2者的任意一个地方创建文件的时候,可以利用File的构造函数,把上面2个函数获取的路径传递给构造函数,再加上文件名,就可以了:
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里面提取名字作为文件名,然后用这个名字在APP可用的内部缓存路径下面创建一个缓存文件。
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使用的内部存储器的路径,是依据APP的包名有android文件系统指定的。从技术角度讲,如果把APP的内部存储的文件设置为可读,那么其他的APP也可以读取到这些文件的。但是前提是其他的APP要知道你的包名和里面创建的文件的文件名。其他APP不可以浏览你的APP使用的内部存储路径,也不可以读写你APP的文件,除非你的APP明确指出APP创建的文件是可读写的。如果你在你创建的内部存储器里面的文件都使用了MODE_PRIVATE属性,那么他们就永远也没办法被其他的APP获取到。
保存文件到外部存储器
因为外部存储器不是总是可用的,比方说被移除,或者链接到PC作为usb存储器使用,所以在使用之前要检查它是否可用。可以调用
getExternalStorageState()来判断当前外部存储器的状态。如果返回的值等于
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;
}
尽管外部设备是可以被用户或者其他的APP更改,在这里面,依然可以存放这2中属性的文件:
公有文件:文件对其他的APP或者用户都是可用的。当你的应用卸载的时候,这些文件会继续保留着。例如你的APP拍到的照片或者是其他的下载文件。
私有文件:你的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;
}
如果想要在外部存储器上保存一个私有文件,可以使用
getExternalFilesDir()获取路径,同样传递给一个参数来指明文件类型。用这种方法创建的路径都是添加到包含你的所有的外部文件的那个父路径下。当你的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()来创建私有文件,使用API提供的类型参数来指定你的文件的类型都很重要,例如
DIRECTORY_PICTURES。这种方式可以保证你的文件会被系统自动处理归类。例如,如果用
DIRECTORY_RINGTONES来指定你保存的文件类型,系统在扫描你的文件时候,会把这个文件当做铃声而不是音乐文件。
查询剩余空间
有时候需要预先知道存储空间剩余的大小,防止存储大文件出现IO异常,存储空间不够用的情况。可以调用
getFreeSpace()
或者getTotalSpace()来获取的空间大小。前者提供当前可用的空间,后者获取实际上的总的存储空间。系统不保证你的可以写的文件大小和
getFreeSpace获取到的一样大,如果你获取到的比你要写入的文件大一些,或者存储盘距离满还有90%的空间,一般都没什么问题,其他情况下,最好不要往存储器写数据了。
注意:也不是说每次写之前必须检查可用空间,可用在写的过程中捕获IO异常。
删除文件
不需要的时候要删除创建的文件。最简单的方法是用File打开一个文件,然后调用delete方法。
myFile.delete();
如果是存储在内部存储器上的,可以利用Context定位文件,调用deleteFile()删除文件。
myContext.deleteFile(fileName);
同样的记住要手动的删除getCacheDir()下创建的不需要临时文件。