Android 文件存储

1.IO文件读取

IO流类图
IO流
分类

按照读取数据类型不同分为字节流字符流
按照数据流向不同分为输入流和```输出流

常用方式
字节流FileInputStream 和 FileOutputStream文件读写
//写入文件
 private void writeFile() {
        FileOutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(path);
            String str = "asdasdadadad";
            outputStream.write(str.getBytes());
            outputStream.close();
        } 
       ...注意异常处理
    }
//读取文件
FileInputStream inputStream = null;
try {
      inputStream = new FileInputStream(file);
      StringBuilder builder = new StringBuilder();
      int i = 0;
      while ((i = inputStream.read() )!= -1){
           builder.append((char)i);
       }
      //文件信息
      builder.toString();
     inputStream.close();
}..异常处理
BufferedOutputStream与BufferedInputStream

BufferedInputStream是带缓冲区的输入流,默认缓冲区大小是8M,能够减少访问磁盘的次数,提高文件读取性能;BufferedOutputStream是带缓冲区的输出流,能够提高文件的写入效率。BufferedInputStream与BufferedOutputStream分别是FilterInputStream类和FilterOutputStream类的子类,实现了装饰设计模式。

FileOutputStream outputStream = null;
BufferedOutputStream bufferedOutputStream;
 try {
       outputStream = new FileOutputStream(path);
       bufferedOutputStream = new BufferedOutputStream(outputStream);
       String str = "sssssssasdafafasda";
       bufferedOutputStream.write(str.getBytes());
       //写完毕后要将数据刷新到文件中
       bufferedOutputStream.flush();
       outputStream.close();
       bufferedOutputStream.close();
 } //异常处理
FileInputStream inputStream = null;
BufferedInputStream bufferedInputStream;
try {
      inputStream = new FileInputStream(file);
      bufferedInputStream = new BufferedInputStream(inputStream);
      StringBuilder builder = new StringBuilder();
      //缓冲区 不设置默认为8K
      byte[] bytes = new byte[1024];
      int i = 0;
      while ((i = bufferedInputStream.read(bytes) )!= -1){
           builder.append(new String(bytes));
       }
      builder.toString()
      inputStream.close();
}//异常处理
字符流操作文件

FileWriterFileReader读写文件

    //写文件
    private void writerFile() {
        FileWriter writer = null;
        try {
            writer = new FileWriter(path);
            String str = "中文测试呢";
            writer.write(str);
            writer.close();
        } //异常处理
    }
//读文件
private void readerFile() {
        FileReader reader = null;
        try {
            reader = new FileReader(file);
            StringBuilder builder = new StringBuilder();
            int i = 0;
            while ((i = reader.read() )!= -1){
                builder.append((char)i);
            }
            mResult.setText(builder.toString());
            reader.close();
        } //异常处理
    }
BufferedReader和BufferedWriter,带缓冲区的字符流。
  private void bufferReaderFile() {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(file));
            StringBuilder builder = new StringBuilder();
            String str = "";
            while ((str = reader.readLine()) != null){
                builder.append(str);
            }
            mResult.setText(builder.toString());
            reader.close();
        } //异常处理
    }
    private void  bufferWriterFile() {
        BufferedWriter writer = null;
        try {
            writer = new BufferedWriter(new FileWriter(path));
            String str = "BufferedWriter测试呢";
            writer.write(str);
            writer.close();
        } //异常处理
    }
InputStreamReader将字节流转换为字符流
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "GB2312");
ObjectOutputStream与ObjectInputStream序列化与反序列化
    //序列化
    private void writeObject(){
        ObjectOutputStream objectOutputStream = null;
        try {
            objectOutputStream = new ObjectOutputStream(new FileOutputStream(path));
            Student student = new Student();
            objectOutputStream.writeObject(student);
            objectOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (null != objectOutputStream){
                try {
                    objectOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //反序列化
    private void readObject(){
        ObjectInputStream objectInputStream = null;
        try {
            objectInputStream = new ObjectInputStream(new FileInputStream(path));
            Student student = (Student) objectInputStream.readObject();
            mResult.setText(student.toString());
            objectInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (null != objectInputStream){
                try {
                    objectInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
1.序列化 ID 问题

虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID = 1L)。

2.静态变量序列化

序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。

3.父类的序列化与 Transient 关键字

一个子类实现了 Serializable 接口,它的父类都没有实现 Serializable 接口,序列化该子类对象,然后反序列化后输出父类定义的某变量的数值,该变量数值与序列化时的数值不同。
解决方法:要想将父类对象也序列化,就需要让父类也实现Serializable 接口。或者有默认的无参的构造函数,在构造函数中完成初始化。
Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。

4.自定义序列化过程

虚拟机会试图调用对象类里的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化,如果没有这样的方法,则默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。用户自定义的 writeObject 和 readObject 方法可以允许用户控制序列化的过程。

   //序列化过程
    private void writeObject(ObjectOutputStream out) {
        try {
            ObjectOutputStream.PutField putFields = out.putFields();
            career = "new Student";//模拟加密
            putFields.put("career", career);
            out.writeFields();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
   //反序列化过程
    private void readObject(ObjectInputStream in){
        try {
            ObjectInputStream.GetField getField = in.readFields();
            career = (String) getField.get("career", "");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
5.序列化存储规则

Java 序列化机制为了节省磁盘空间,具有特定的存储规则,当写入文件的为同一对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用,新增一些控制信息。反序列化时,恢复引用关系该存储规则极大的节省了存储空间。

2.sqlite数据库

sqlite数据库的使用:
1、继承SQLiteOpenHelper

public class DbHelper extends SQLiteOpenHelper {

    //数据库名称
    private final static String dbName = "test.db";
    //表名称
    public final static String dbTable = "testTable";

    public DbHelper(Context context){
        this(context,dbName,null,1);
    }

    public DbHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        //上下文,数据库名称,游标工厂,版本号
        super(context, name, factory, version);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        //创建表操作
        String sql = "create table if not exists "+ dbTable+"(Id integer /*primary key*/ , Name text ,
 Price integer , Age integer)";
        sqLiteDatabase.execSQL(sql);
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        //更新操作一般有如下步骤,本次演示只默认删除重建
        if (oldVersion != newVersion) {
            //1.创建临时表保存数据
            //2.删除现有表
            //3.创建新表
            //4.复制临时表数据至新表
            //5.删除临时表
            sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + dbTable);
            onCreate(sqLiteDatabase);
        }
    }
}

2、创建数据库操作类,持有SQLiteOpenHelper子类对象,操作数据增删改查:

   //插入
   public void insert(){
        SQLiteDatabase writableDatabase = mDbHelper.getWritableDatabase();
        writableDatabase.beginTransaction();
        writableDatabase.execSQL("insert into " + mDbHelper.dbTable+"(Id,Name,Price,Age)values (1,'xj2',12,18)");
        writableDatabase.execSQL("insert into " + mDbHelper.dbTable+"(Id,Name,Price,Age)values (2,'xj3',13,18)");
        writableDatabase.execSQL("insert into " + mDbHelper.dbTable+"(Id,Name,Price,Age)values (3,'xj4',14,18)");
        writableDatabase.execSQL("insert into " + mDbHelper.dbTable+"(Id,Name,Price,Age)values (4,'xj5',15,18)");
        writableDatabase.execSQL("insert into " + mDbHelper.dbTable+"(Id,Name,Price,Age)values (5,'xj6',16,18)");
        writableDatabase.execSQL("insert into " + mDbHelper.dbTable+"(Id,Name,Price,Age)values (6,'xj7',17,18)");
        writableDatabase.setTransactionSuccessful();
        writableDatabase.endTransaction();
    }
//查询
public String  query(){
        SQLiteDatabase writableDatabase = mDbHelper.getWritableDatabase();
        Cursor query = writableDatabase.query(mDbHelper.dbTable, null, null, null, null, null, null);
        StringBuilder builder = new StringBuilder();
        while (query.moveToNext()){
           //查询数据
           int id = query.getInt(query.getColumnIndex("Id"));
        }
        return builder.toString();
    }
其余方法等同.数据库详细语法后续再详细学习。

数据库操作有两种方式,一种如insert方法一样执行SQL语句,另一种如query方法一样使用SQLiteDatabase 封装过的方法。
sqlite是线程安全的吗
Android中SQLiteDatabase为sqlite提供了线程安全的保证,因此Android中sqlite数据库是线程安全的。
sqlite是线程安全的吗
数据库保存在/data/data/pageName/databases/dbName.db,其中pageName为包名,dbName为数据库名称。

3.SharedPreferences

SharedPreferences是Android轻量级的存储方式,使用方式如下

    public void saveSp(){
        SharedPreferences preferences = getSharedPreferences("perfer_file",MODE_PRIVATE);
        SharedPreferences.Editor edit = preferences.edit();
        edit.putString("key",value);
        edit.putInt("key",value);
        ....
        edit.apply();/edit.commit();
    }

    public void readSp(){
        SharedPreferences preferences = getSharedPreferences("xujie,",MODE_PRIVATE);
        String value= preferences.getString("key", defaultValue);
        int value= preferences.getInt("key", defaultValue);
        ...
    }

存取使用SharedPreferences.Editor来完成。数据存储在/data/data/pageName/shared_prefs/perfer_file.xml里面,结构如下:

<map>
    <string name="key">value</string>
    <int name="key" value="value" />
</map>

注意点:
1.SharedPreferences对大小没有限制,但是如果存储数据过大会导致性能问题,因此要存储合理的数据。
2.SharedPreferences只能存储基本类型的数据。
3.SharedPreferences线程不安全。

4.ContentProvider

四大组件之一,数据通过ContentProvider存储在别的工程中。可参考四大组件文章。

5.网络存储

数据存储在网络上,通过Http等协议获取数据,常用库为Okhttp等。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容