Android数据持久化技术之文件存储
概述
最近项目中用到了文件读取和存储相关的技术,之前都是用到什么去网上查找,没有做过系统化的学习和总结,趁着项目的间歇花点时间把这块知识点总结一下。
File类
File类是用来描述一个文件或者文件夹的。 通过File对象可以创建或是删除文件或文件夹,获取文件或者文件夹的名字、路径、是否可读写等相关数据,设置相关属性等。如果需要读取文件的内容数据,就需要使用到IO流技术。
各级目录及获得方法如下:
($rootDir)
+- /data -> Environment.getDataDirectory()
| |
| | ($appDataDir)
| +- data/com.srain.cube.sample
| |
| | ($filesDir)
| +- files -> Context.getFilesDir() / Context.getFileStreamPath("")
| | |
| | +- file1 -> Context.getFileStreamPath("file1")
| | ($cacheDir)
| +- cache -> Context.getCacheDir()
| |
| +- app_$name ->(Context.getDir(String name, int mode)
|
| ($rootDir)
+- /storage/sdcard0 -> Environment.getExternalStorageDirectory()
| / Environment.getExternalStoragePublicDirectory("")
|
+- dir1 -> Environment.getExternalStoragePublicDirectory("dir1")
|
| ($appDataDir)
+- Andorid/data/com.srain.cube.sample
|
| ($filesDir)
+- files -> Context.getExternalFilesDir("")
| |
| +- file1 -> Context.getExternalFilesDir("file1")
| +- Music -> Context.getExternalFilesDir(Environment.Music);
| +- Picture -> ... Environment.Picture
| +- ...
|
| ($cacheDir)
+- cache -> Context.getExternalCacheDir()
|
+- ???
IO流技术
IO流技术主要是解决设备与设备之间的数据传输问题(硬盘 -> 内存,内存 -> 硬盘)。按照数据的流向划分可以分为输入流和输出流;按照处理的单位可以分为字节流和字符流。其中,字节流读取得都是文件中二进制数据,读取到二进制数据不会经过任何的处理。字符流读取的数据是以字符为单位的 。字符流也是读取文件中的二进制数据,不过会把这些二进制数据转换成我们能识别的字符。简单总结就是字符流等于解码后的字节流 。

字节流数据读取写入
Google提供了InputStream和OutputStream两个抽象类作为所有字节流输入输出类的基类。FileInputStream和FileOutputStream就是分别派生于他们,也可以用自带内部缓存数组的BufferedInputStream和BufferedOutputStream来实现字节流数据的读取和写入。两组方法的差异在于BufferedInputStream和BufferedOutputStream内部包含了一个字符数组buf用来缓存数据,减少与底层的IO操作提升效率。如果使用FileInputStream和FileOutputStream每次读取与BufferedInputStream和BufferedOutputStream内部buf相同大小的数据,两组方法的效率几乎是没有差别的。
FileInputStream
通过FileInputStream对象可以获得文件系统中某个文件的输入字节流。
-
构造方法
构造方法
-
公开方法
公开方法
-
示例
/**
* Read byte stream from file
*
* @param fileName The name of the file to open.
*
* */
public static String readByteData(String fileName) {
FileInputStream fis = null;
StringBuffer content = new StringBuffer();
try {
File file = new File(fileName);
if(!file.exists()) {
return null;
}
fis = new FileInputStream(file);
int length;
byte[] buf = new byte[1024];
while ((length = fis.read(buf)) != -1) {
content.append(new String(buf,0,length));
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if(fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return content.toString();
}
FileOutputStream
通过FileOutputStream对象可以把字节流数据写入到文件系统中的某个文件。
-
构造方法
构造方法
-
公开方法
公开方法
-
示例
/**
* Write byte stream to file
*
* @param fileName The name of the file to open.
* @param content The input content
*
* */
public static void writeByteData(String fileName, String content) {
FileOutputStream fos = null;
try {
File file = new File(fileName);
if(!file.exists()) {
file.createNewFile();
}
fos = new FileOutputStream(file);
fos.write(content.getBytes());
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if(fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
BufferedInputStream
BufferedInputStream内部定义了一个buf成员变量,他是一个字节数组,可以自定义大小,默认大小为8*1024。当通过read方法读取数据的时候,会从输入流中一次性读取大量字节填充到buf中,然后再从buf中读取数据做相应操作。当buf中的数据都读取完了,会再次从输入流中一次性读取大量字节填充到buf中,循环往复,直到文件中所有的字节都被读取完。
-
构造方法
构造方法
-
公开方法
公开方法
-
示例
/**
* Read byte stream from file by buffered stream
*
* @param fileName The name of the file to open.
*
* */
public static String readByteDataWithBuffer(String fileName) {
FileInputStream fis = null;
BufferedInputStream bis = null;
StringBuffer content = new StringBuffer();
try {
File file = new File(fileName);
if(!file.exists()) {
return null;
}
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
int length;
byte[] buf = new byte[1024];
while ((length = bis.read(buf)) != -1) {
content.append(new String(buf,0,length));
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
if(fis != null) {
fis.close();
}
if(bis != null) {
bis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return content.toString();
}
BufferedOutputStream
BufferedOutputStream内部定义了一个buf成员变量,他是一个字节数组,可以自定义大小,默认大小为8*1024。当通过write方法写入数据的时候,会把数据写到buf中缓存,当buf被填满才会一次性写入目的文件。然后继续把输入数据写到buf中缓存,当buf被填满一次性写入目的文件,循环往复,直到所有数据都被写入目的文件。
-
构造方法
构造方法
-
公开方法
公开方法
-
示例
/**
* Write byte stream to file by buffered stream
*
* @param fileName The name of the file to open.
* @param content The input content
*
* */
public static void writeByteDataWithBuffer(String fileName, String content) {
FileOutputStream fos = null;
BufferedOutputStream bos = null;
try {
File file = new File(fileName);
if(!file.exists()) {
file.createNewFile();
}
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
bos.write(content.getBytes());
bos.flush();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
if(fos != null) {
fos.close();
}
if(bos != null) {
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
字符流数据读取写入
Google提供了Reader和Writer两个抽象类作为所有字符流输入输出类的基类。FileReader和FileWriter就是分别派生于他们,也可以用自带内部缓存数组的BufferedReader和BufferedWriter来实现字符流数据的读取和写入。两组方法的区别与上面字节流数据读取写入中介绍的两组方法的区别相同。
FileReader
-
构造方法
构造方法
-
公开方法
用于读取字符流数据文件的封装类,在其构造方法中指定了默认的字符编码和默认的字符缓存区的大小,没有重写父类方法,也没有额外的方法提供。 -
示例
推荐使用BufferedReader方法
FileWriter
-
构造方法
构造方法
-
公开方法
用于写入字符流数据文件的封装类,在其构造方法中指定了默认的字符编码和默认的字符缓存区的大小,没有重写父类方法,也没有额外的方法提供。 -
示例
推荐使用BufferedWriter方法
BufferedReader
-
构造方法
构造方法
-
公开方法
公开方法
-
示例
/**
* Get character byte stream from file
*
* @param fileName The name of the file to open; can not contain path separators.
*
* */
public static String readCharacterData(String fileName) {
BufferedReader br = null;
StringBuffer sb = new StringBuffer();
try{
File file = new File(fileName);
if(!file.exists()) {
return null;
}
br = new BufferedReader(new FileReader(fileName));
String content;
while ((content = br.readLine()) != null ) {
sb.append(content);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
BufferedWriter
-
构造方法
构造方法
-
公开方法
公开方法
-
示例
/**
* Input character stream to file
*
* @param fileName The name of the file to open; can not contain path separators.
* @param content The input content
*
* */
public static void writeCharacterData(String fileName, String content) {
BufferedWriter bw = null;
try {
File file = new File(fileName);
if(!file.exists()) {
file.createNewFile();
}
bw = new BufferedWriter(new FileWriter(file));
bw.write(content);
bw.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if(bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
文件拷贝
/** buffer size in bytes */
final static int BUFFER_SIZE = 1024*8;
/**
* copy file using BufferedInputStream & BufferedOutputStream
* @param src copy from file
* @param dest copy to file
* @return;
*/
public static void copyWithBufferedStream(File src, File dest){
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
if(!dest.exists()) {
dest.createNewFile();
}
bis = new BufferedInputStream(new FileInputStream(src));
bos = new BufferedOutputStream(new FileOutputStream(dest));
byte[] buffer = new byte[BUFFER_SIZE];
int copySize;
while ((copySize = bis.read(buffer)) != -1){
bos.write(buffer, 0, copySize);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(bis != null) {
bis.close();
}
if(bos != null) {
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
总结
本文对Android数据持久化技术之文件存储中用到的基本知识做了梳理,并提供了相应的示例。关于字符流输入输出的介绍没有字节流输入输出写的详细,原理都是一样的,可以参照字节流输入输出。关于同一流派不同方法之间效率的对比可以参看FileInputStream 与 BufferedInputStream 效率对比
,里面写的比较详细。
参考文献
I/O流之进步认识、InputStream以及FileInputStream
IO流(操作文件内容): 字节流
Java字节流:FileInputStream FileOutputStream
java核心技术之IO流(五)BufferedReader和BufferedWriter
BufferedReader、FileReader、FileInputStream的区别
FileInputStream 与 BufferedInputStream 效率对比