Android使用的文件系统和其他平台的基本磁盘的文件系统很相似。这里将要介绍如何使用File API在Android文件系统中读写文件。
File对象适合按顺序读写大量的数据。例如,适合图片文件或者其他在网络上交换的东西。
这里将要展示基本的文件相关的任务。这里假设你熟悉基本的Linux文件系统和标准的java.io中的输入输出。
选择内部或外部存储
所有的Android设备由两种存储位置:内部存储和外部存储。这些名字来源于早期的Android,大多数提供内置的不可拆解的存储器(internal storage),加上一个可移除的存储介质比如micro SD
卡(external storage)。有些设备把永久存储空间分为“内部”和“外部”,就算没有可移除的存储介质,总有两个存储空间不管外部存储是否存在API的行为是一样的。下面列出了两种存储空间
的简介。
内部存储:
1、总是可用的
2、这里存储的文件默认是只能被你的app访问到。
3、当用户卸载了app,系统会把app所有的文件从内部存储上删除。
当你要确保无论是用户还是其他app都不能访问你的文件时,内部存储是最好的地方
外部存储:
1、不总是可用的,因为用户可能会把外部存储当作USB使用或者有时候会从设备上拿走。
2、谁都可以读,这里保存的文件可以总是被读,不在你的控制之中。
3、当用户卸载你的app时,系统只会删除getExternalFilesDir()文件夹中的文件。
如果文件没有访问权限,或者你向和其他app分享,或者可以让用户通过电脑访问,外部存储是最好的地方。
提示:虽然app是默认安装到内部存储上,但是你一个指定manifest中的android:installLocation让app了以安装到外部存储上。当APK很大或者外部存储大小比内部存储大小大时,用户很喜欢这个选择。
更多信息,查看App Install Location。
在内部存储上保存文件
当在内部存储上保存文件时,你可以通过File的下面两个方法获获得文件夹:
getFilesDir()
返回一个File,代表app的内部文件夹
getCacheDir()
返回一个File,代表app的临时cache文件的内部文件夹。确保在文件不需要的时候删除它们,并且在任何时候要限制使用的内存在一个合理的大小,比如1MB。当系统运行到存储不够时,可能不提
示警告就会删除你的cache。
要在这些文件夹中创建一个新文件,你可以使用File()构造方法,传递由上面方法提供的指定内部文件夹的File,例如:
1 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不能浏览你的内部文件夹也不能读写除非你明确的设置文件为可读或可写。所以只要你在内部存储时文件使用MODE_PRIVATE模式,其他app就一直不能进入它们。
在外部存储上保存文件
由于外部存储可能不可用(比如用户把存储安装到了PC或者拿走了提供外部存储的SD卡),每次进入之前都要检查是否可用。可以通过getExternalStorageState()来查询外部存储状态。如果返回的状态是
MEDIA_MOUNTED,那么可以读写文件。例如,下面的方法可以判断存储是否可用:
/* 查看外部存储是否可读可写 */
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;
}
虽然外部存储可以被用户和其他app修改,你可以在这里存储两类文件:
公有文件
可被其他app和用户自由使用的文件。当用户卸载app后,这些文件仍然可以被用户使用。
比如:你的app获取的照片,或者下载的文件。
私有文件
属于你的app的并且卸载时需要删除的文件。虽然因为是在外部存储上这些文件技术上来说是可以被用户和其他app进入的,但是这些文件对于你的app之外的用户没有任何实际意义。当用户卸载app时,系统
会删除app外部存储文件夹下的所有文件。
比如:app另外下载的资源或者临时媒体文件。
如果需要在外部存储中保存公有文件,使用getExternalStoragePublicDirectory()方法来获得代表外部存储上的适合的文件夹的File。这个方法带有一个指定你要保存的文件类型的参数,这样可以在逻辑上
和其他公有文件进行管理,比如DIRECTORY_MUSIC或者DIRECTORY_PICTURES。例:
public File getAlbumStorageDir(String albumName) {
// 获得用户公有图片文件夹.
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
return file;
}
如果要保存app的私有文件,可以调用getExternalFilesDir()然后传递一个你喜欢的表示文件夹类型的名字来获得合适的文件夹。每个这样创建的文件夹会被加到一个父文件夹中,父文件夹包含了你的app的
所有外部存储文件,它们会在用户卸载app时被系统删除。
例如,这里的方法可以用来创建一个私有的相册文件夹:
public File getAlbumStorageDir(Context context, String albumName) {
// 获得app私有图片文件夹.
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()还是为app私有文件使用getExternalFilesDir(),使用API常量比如DIRECTORY_PICTURES作为文件夹名很重要。这些文件夹名确保了系统会
合适的处理这些文件。例如,DIRECTORY_RINGTONES中的文件会被系统认为是铃声而不是音乐。
查询可用空间
如果提早知道保存了多少数据,可以知道是否有充足的空间可用,不会通过调用getFreeSpace()或getTotalSpace()导致一个IOException。这些方法分别可以提供存储卷轴上当前的可用空间个总空间。这些信
息对于避免存储超过存储极限很有用。
然而,系统并不保证你可以写getFreeSpace()指示的那么多的字节。如果返回的数字比要存储的数据大几MB,或者文件系统存储量小于90%,那么存储会很安全。否则,可能不能向存储中写入。
注意:不需要在存储文件时查看可用空间,可以在try中写文件,然后catch IOException如果出现异常。当不知道需要多大空间时也可以这么做。例如,在存储之前把文件编码从PNG转换成JPEG,预先是不知道文件大小的。
删除文件
不在需要的文件要删除。最直接的删除文件的方式是调用已打开文件自己的delete()。
1 myFile.delete();
如果文件保存在内部存储上,也可以用Context来定位然后调用deleteFile()来删除文件:
1 myContext.deleteFile(fileName);
注意:当用户卸载app时,Android系统会删除下面的文件:
所有保存在内部存储上的文件
所有使用getExternalFilesDir()保存在外部存储上的文件。
但是,应该按照基本规则手动删除用getCacheDir()创建的缓存文件,和其他不再需要的文件。
下面有一个小马同学整理的一段代码:
package com.example.valueres;
import java.io.File;
import java.io.IOException;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
/**
*/
public class FileTestActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//写的时候不带后缀名,其实这个要写的而且安卓中不建议用此种写法,支持使用Environment获取路径
String path = "/sdcard/fileName";
File file = new File(path); //不多讲
//下面是File类的一些静态属性
//以前喜欢自己用"/"去拼接,虽然不错,但最好用其自带的方法
//file.separator;
//举两种 写法:
//方法一:
String path2 = Environment.getExternalStorageDirectory().getPath()+"/"+"XiaoMa.txt";
File fileName = new File(path2);
//此处可以添加对文件的操作,IO流
//方法二:
String path3 = Environment.getExternalStorageDirectory().getPath()+File.separator+"XiaoMa.txt";
File fileNam = new File(path3);
//此处可以添加对文件的操作,IO流
/**
* 大家是不是发现这两种写法第二种多此一举?其实并非如此的
*,在Android中,官方鼓励支持使用
* File.separator来插入"/"符号
* 因为:不同的机型配置系统等各种因素下,"/"符号的转义字符也会不同*
*,所以为保险起见,
* 建议朋友们使用File.separator来区别
*/
//下面讲下文件的创建是怎么一回事
//Android 中创建文件的方式有很多种,讲下
//我们经常用到的几种哦,吼吼
File file3 = new File(path); //path为要创建文件的全路径
//相关路径可查看API
//创建一个临时文件
//如果让我们自己创建临时文件时,我们还得拼接文件名
//Android为我们提供了更快捷的方式
//prefix代表:文件名前缀,suffix代表:生成临时文件的格式,
//如:.txt .amr .jpg .png等等
try {
//此处未指定路径,它会默认创建在SD卡根目录下
File tempFile = File.createTempFile("前缀", "格式"); //记得加入try语句块中
//如果想要指定路径,则可以这样写
File tempFile2 = File.createTempFile("前缀", "格式",new File("指定临时文件路径"));
} catch (IOException e) {
e.printStackTrace();
}
//创建文件的方式有三种:要看仔细咯
File file2 = new File(path3);
if(!file2.exists()){
//方式一:
try {
file2.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//方式二:
//file2.mkdir();
//方式三:
//file2.mkdirs();
/**
*以上创建文件方式好像也没多大不同之处。
*创建文件的时候随便调用这三个方法其中之一
*但后来发现还是有明显区别的,现在贴一段官方API说明
*/
/**
*小述:讲下“/”与“\”的不同,在创建文件时不能使用使用windows方式,
*也就是"\",除非你转义有linux方式的话就直接用“/”,省了麻烦,
*还不会出错转义错误等问题。
*/
/**
* createNewFile()
Creates a new, empty file on the file system according to the path information
stored in this file.
这个方法指在指定路径下创建一个empty(空的)文件
*/
/**
* mkdir()
Creates the directory named by the trailing filename of this file.
这个方法指:创建一个指定目录下的文件,但这个方法不能创建多个目录,
一般都使用加S的,这个尽量少用,一般私有文件及私有目录的创建用这个方法
*/
/**
* mkdirs()
Creates the directory named by the trailing filename of this file,
including the complete directory path required to create this
directory.
这个方法指:创建一个指定目录下的文件,与上面不同的是,
此方法可以创建多个目录哦,常用的创建文件的方法
*/
/**
* 最后,讲下isDirectory() 与 isFile()的小区别
就是:是否是文件夹,isFile是否是文件。
*/
}
}
}
最后,不要忘记加Android中文件操作的权限。
<!--往sdcard中写入数据的权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<!--在sdcard中创建/删除文件的权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission>