持久化技术
所谓数据持久化,就是将那些内存中的瞬时数据保存到存储设备中,保证在断电的情况下,数据依然存在。
持久化技术则是为数据在持久状态和瞬时状态间转换提供机制。
https://blog.csdn.net/nishigesb123/article/details/88875292#t6
一文中涉及了SharedPreferences,这是安卓中比较常见的数据存储的解决方案(数据持久化解决方案)
而这篇文章的文件管理,也是一种比较常见的数据存储的解决方案,或者说是最基本的,实际上在之前已经有过不少案例,比如AsyncTask的文章就有用到过。
除此之外还有一种方式是通过安卓的SQLite数据库,已经🐎出来了,可以参考下面的链接
https://blog.csdn.net/nishigesb123/article/details/89202726
文件存储
使用内部存储器
你可以直接保存文件在设备的内部存储。默认情况下,文件保存在你的应用程序的内部存储,其他应用程序或用户不能访问。当用户卸载你的应用程序中,这些文件被删除。
在内部存储创建并写入私有文件
1、调用openFileOutput(name,model)方法 返回FileOutputStream
name参数:用于指定文件名称,不能包含路径分隔符 “/”,如果文件不存在,会自动创建它。创建的文件保存在/data/data/< package name>/files/目录中
model参数:指的是使用模式,有如下三种
MODE_ PRIVATE
//私有,创建此文件的应用能够使用,其他应用不能访问,写入文件会覆盖原来的内容。
MODE_ APPEND
//私有,在原有内容上增加数据。
MODE WORLD READABLE, MODE WORLD WRITEABLE
//可以被其他应用读取或写入(API17版本中已废弃)不建议使用
2、调用write方法把数据写入文件
3、调用close方法关闭流
package com.example.a4_9file;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void inner(View v){
try {
//没有会自动创建
OutputStream outputStream = openFileOutput("inner.txt", Context.MODE_APPEND);
String info="在内部存储创建并写入私有文件";
byte[] bytes=info.getBytes();
//把数据写入文件
outputStream.write(bytes,0,bytes.length);
//关闭流
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
效果如下: 可以看到文件被创建出来了,且内容也是对应的内容。
再次点击,会在后面追加一段文本——因为选择的模式是MODE_APPEND,如果选择MODE_PRIVATE,内容将不会改变。
在内部存储读取私有文件
准备一个Button,点击事件为inner_read,实现读取私有文件代码如下:
public void inner_read(View v){
try {
InputStream in =openFileInput("inner.txt");
byte[] bytes=new byte[1024];
StringBuffer sb=new StringBuffer();
int len=-1;
while ((len= in.read(bytes))!=-1){
sb.append(new String(bytes,0,len));
}
in.close();
Toast.makeText(this, sb, Toast.LENGTH_SHORT).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
效果如下:文本内容没改 因为刚才写入了两次:) 所以会文本重复出现
读原生文件
res/raw/目录下的文件称为原生文件,该文件为只读文件,不能对其执行写入操作。
可以通过openRawResource()来传入R.raw. < filename>资料ID
即可返回一个InputStream
准备Button和之前一样,需要额外准备一个test.txt文件,创建在res/raw文件下
代码很简单,改动基本只有InputStream in = getResources().openRawResource(R.raw.test)一句
public void protosomatic(View v){
try {
InputStream in = getResources().openRawResource(R.raw.test);
byte[] bytes=new byte[1024];
StringBuffer sb=new StringBuffer();
int len=-1;
while ((len= in.read(bytes))!=-1){
sb.append(new String(bytes,0,len));
}
in.close();
Toast.makeText(this, sb, Toast.LENGTH_SHORT).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
保存内部缓存文件
如果你只是想缓存一些数据,而不是永久保存。
可以使用getCacheDir打开文件所在的目录(/data/data/< package name>/cache)
- 其代表内部应用程序应保存临时缓存文件,当用户卸载你的应用程序,这些文件被删除。
- 当设备的内部存储空间不足时,系统也会删除这些缓存文件以回收空间。
- 实际上,你应该自己维护缓存文件,并保持在一个合理的消耗空间的限制。
...依旧准备一个Button(点击事件为cahe)
public void cache(View v){
//文件目录
//String temp= getCacheDir() + "/temp.tmp";
try {
//创建一个临时文件
//后缀名不需要 虽然指定了名字但是后面还是会跟上一串内容...即实际上文件名会是tempxxxx.temp
File temp=File.createTempFile("temp",null,getCacheDir());
//流
FileOutputStream out =new FileOutputStream(temp);
PrintStream ps =new PrintStream(out);
//打印
ps.print("测试存内部缓存文件");
ps.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
可以看到 点击后生成了tmp文件
补充
除了上面的案例,还有一些方法可以参考,如下:
使用外部存储器
所有兼容Android的设备都支持一个可共享的“外部存储(external storage)",可用来保存文件。
- 外部存储可以是一个可移动的存储设备(比如SD卡)或者一个内部的(不可移动的)存储。
- 保存在外部存储的文件是可读的。
- 当用于传输数据的USB大容量存储选项启用时,用户能够在计算机上修改它们。
权限
为了在外部存储读或写文件,必须要在配置清单文件中添加相应的权限。
//读
android.pernission.READ_EXTERNAL_STORAGE
//写
android.permission.WRITE_EXTERNAL_STORAGE
注意:
在4.4中,如果这些外部存储是私有化的,你可以不需要添加这些权限。
检查媒体的可用性
在使用外部存储来保存数据前:
我们应该先使用getExternalStorageState来检查当前设备是否存在外部存储设备(SDCard) ,否则将会发生意外。
还是要准备一个Button,事件为ifexist
//是否存在外部存储设备
public void ifexist(View v){
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
Toast.makeText(this, "有SDCard", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(this, "没有SDCard", Toast.LENGTH_SHORT).show();
}
}
还可以通过下面的语句来判断,SDCard是否只读
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY)){
Toast.makeText(this, "SDCard只读", Toast.LENGTH_SHORT).show();
}
访问SDcard路径
以前的Android(4.1之前的版本)中:
SDcard路径通过"/sdcard" 或 "/mnt/sdcard" 来表示。
而在Jelly Bean(Android – 4.3 )系统中改为了"/storage/sdcard0"
以后可能还会有多个SDcard的情况,目前为了保持和之前代码的兼容,sdcard路径做了link映射。
为了使代码更加健壮并能兼容以后的Android版本和新设备。
请通过Environment.getExternalStorageDirectory().getPath()来获取sdcard路径。
在原来的代码基础上增添一句 System.out.println(Environment.getExternalStorageDirectory().getPath());
//是否存在外部存储设备
public void ifexist(View v){
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
Toast.makeText(this, "有SDCard", Toast.LENGTH_SHORT).show();
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY)){
Toast.makeText(this, "SDCard只读", Toast.LENGTH_SHORT).show();
}else{
//访问SDcard路径
System.out.println(Environment.getExternalStorageDirectory().getPath());
}
}else {
Toast.makeText(this, "没有SDCard", Toast.LENGTH_SHORT).show();
}
}
如果您需要往sdcard中保存特定类型的内容,可以考虑使用
Environment.getExternalStoragePublicDirectory(String type)函数
该函数可以返回特定类型的目录,目前支持如下类型:
DIRECTORY_ALARMS // 警报的铃声
DIRECTORY_DCIM // 相机相摄的图片和视频保存的位置
DIRECTORY_DOWNLOADS // 下载文件保存的位置
DIRECTORY_MOVIES // 电影保存的位置,比如通过google play下载的电影
DIRECTORY_MUSIC// 音乐保存的位置
DIRECTORY_NOTIFICATIONS // 通知音保存的位置
DIRECTORY_PICTURES // 下载的图片保存的位置
DIRECTORY_PODCASTS // 用于保存podcast(博客)的音频文件
DIRECTORY_RINGTONES// 保存铃声的位置
根据这些参数系统会自动扫描,并整理到对应的目录中。
这部分读者可以自行测试。
保存文件私有化
如果希望app的文件不让其他app读取。
可以使用getExteralFilesDir方法(参数为type,默认值为null)
但这个方法,不一定总能够访问sd卡中的信息。
- 如果设备自身已经有了一部分内部存储当做外部存储,那这个方法就不能访问到sd卡。
当应用卸载时系统将会删除以下内容:
- 你保存在内部存储的所有文件
- 所有使用本应用外部存储的私有文件
public void external(View v){
File file = getExternalFilesDir(null);
if (file!=null){
...
}
}
外部存储保存私有缓存文件
长时间保存的数据:
- 可以通过Context.getExternalFilesDir方法获取
- 一般为SDCard/Android/data/你的应用的包名/files/目录
- 可以通过设置->应用->应用详情->清除数据 清除
临时缓存数据:
- 可以通过Context. getExternalCacheDir)方法获取
- 一般为SDCard/Android/data/你的应用包名/cache/目录
- 可以通过设置->应用->应用详情->清除缓存 清除
应用被卸载后,👆两种数据都会被删除
测试代码,自行准备button
//保存文件私有化&外部存储保存私有缓存文件
public void external(View v){
File file = getExternalFilesDir(null);
if (file!=null){
try {
FileOutputStream out =new FileOutputStream(file+"/test.txt");
PrintStream ps =new PrintStream(out);
ps.print("外部存储保存私有缓存文件");
ps.close();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
效果如下:
查询空闲空间
如果你事先知道要存储的数据量的大小,你可以先查询一下可用空间大小,根据可用空间大小来预先判断是否能存入数据,可用避免lOException。
查询可用空间大小可用的方法:
- getFreeSpace//返回剩余空间
- getTotalSpace//返回总空间
但是尽管得知了可用空间大小,并不说明你就可以存入这么多的数据,一般要预留一部分,即实际空间应该比你要存储的数据大小稍大一些才行。所以可能归根到底还是try catch IOException比较实用。