持久化技术——文件存储

持久化技术

所谓数据持久化,就是将那些内存中的瞬时数据保存到存储设备中,保证在断电的情况下,数据依然存在。

持久化技术则是为数据在持久状态和瞬时状态间转换提供机制。

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卡。

当应用卸载时系统将会删除以下内容:

  1. 你保存在内部存储的所有文件
  2. 所有使用本应用外部存储的私有文件
    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比较实用。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云无心鸟知还

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值