Android数据持久化技术详解

数据存储

瞬时数据会在程序关闭时销毁

文件存储

Context类下的文件处理方法
//获取/data/data/<包名>/files
getFilesDir();
//读取files文件夹下的某文件
openFileInput("files文件夹下的某文件");
//写入files文件夹下的某文件,不存在则新建,(private是若存在则覆盖,append是若存在则追加)
openFileOutput("想放在files文件夹下的文件",MODE_PRIVATE);
//获取/data/data/<包名>/子目录
getDir("子目录",MODE_PRIVATE);
//获取文件列表
fileList();
//获取/data/data/<包名>/cache
getCacheDir();


Tips:可以使用Java的File类对文件进一步处理

将数据存入文件

文件位置在设备的/data/data/包名/files/文件名

![](https://juejin.cn/)
private void save(String s) {
    BufferedWriter writer = null;
    try {
        //Context类中的openFileOutput方法返回一个FileOutputStream,参数为文件名,模式
        FileOutputStream fm = openFileOutput("data", MODE_PRIVATE);//拿到字节流
        OutputStreamWriter or = new OutputStreamWriter(fm);//字节流转字符流的桥梁
        writer = new BufferedWriter(or);//转为字符流,效率高
        writer.write(s);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (writer != null)
                writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


从文件中读取
private EditText edit;//为了下次启动在输入框里还原数据

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    edit = findViewById(R.id.edit);
    String data_in = load();
    if(!TextUtils.isEmpty(data_in)){//传入的字符串是null或者是空字符串时都会返回true
        edit.setText(data_in);
        edit.setSelection(data_in.length());//移动光标到文末
    }
}


private String load() {
    BufferedReader reader = null;
    StringBuilder builder = null;
    try {
        //Context类中的openFileInput方法返回一个FileInputStream,参数为文件名
        FileInputStream fm = openFileInput("data");
        InputStreamReader ir = new InputStreamReader(fm);
        reader = new BufferedReader(ir);
        builder = new StringBuilder();

        String next = reader.readLine();
        while (next != null) {
            builder.append(next);
            next = reader.readLine();
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (reader != null)
                reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return builder.toString();
}


SharedPreferences存储

键值对存储

文件位置在设备的/data/data/包名/Shared_prefs/文件名

得到SharedPreferences对象

①Context类中的getSharedPreferences("filename",操作模式)

②Activity类中的getPreferences(操作模式),自动把当前活动的类名当做文件名

将数据存入SP
SharedPreferences.Editor editor = getSharedPreferences("data",MODE_PRIVATE).edit();
editor.putString("name","Ethan");//键值对
editor.putInt("age",18);
editor.apply();//异步提交;commit()是同步提交


从SP中读取
SharedPreferences pref = getSharedPreferences("data",MODE_PRIVATE);
String name = pref.getString("name","");//类似于getOrDefault()
int age = pref.getInt("age",0);


使用JSON存储List
Gson gson = new Gson();
String js = pref.getString("json", "default");//从SP存储中得到json字符串
List<Item> list = gson.fromJson(js, new TypeToken<List<Item>>() { }.getType());//json->List
mEditor.putString("json", gson.toJson(list));//List->json再放回SP存储


SQLite存储

4种数据类型:integerreal(浮点数),textblob(二进制)

设为主键:primary key

自动递增:autoincrement

创建与升级

存储路径/data/data/包名/databases

新建类继承自抽象类SQLiteOpenHelper

public class MyDatabaseHelper extends SQLiteOpenHelper {

    //两条建表语句
    private String createBook="create table Book(id integer primary key autoincrement,author text,price real,pages integer,name text)";
    private String createCategory="create table Category(id integer primary key autoincrement,category_name text,category_code integer)";

    public MyDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        //执行建表语句
        db.execSQL(createBook);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //数据库迭代的最佳写法,每升级一次数据库版本加一条if语句
        //(假设第2版数据库想增加Category表,第3版数据库想为Book表增加category_id列)
        if(oldVersion<=1)
            db.execSQL(createCategory);
        if(oldVersion<=2)
            db.execSQL("alter table Book add column category_id integer");
    }
}


在活动中使用

//最后一个参数如果增加,那么就执行MyDatabaseHelper的onUpgrade方法
MyDatabaseHelper dbHelper=new MyDatabaseHelper(this,"BookStore.db",null,1);
button.setOnClickListener((View v)->{
    dbHelper.getWritableDatabase();//如果存在就返回,不存在则新建;getReadableDatabase()也可
});


增C

在活动中执行,id设置的自动递增所以不用加

public static final class Cols{//内部类,存储各列名,便于拓展
    public static final String AUTHOR="author";
    public static final String NAME="name";
    public static final String PAGES="pages";
    public static final String PRICE="price";
}

SQLiteDatabase db=dbHelper.getWritableDatabase();
ContentValues values1=new ContentValues();
values1.put(Cols.AUTHOR,"Ethan");
values1.put(Cols.NAME,"I'm the king");
values1.put(Cols.PAGES,999);
values1.put(Cols.PRICE,0.1);
//参数为(表名,在未指定添加数据为哪些列自动赋值,ContentValues对象)
db.insert("Book",null,values1);


删D

在活动中执行,把页数大于500的书全删掉

SQLiteDatabase db=dbHelper.getWritableDatabase();
db.delete("Book",Cols.PAGES+">?",new String[]{"500"});//之所以使用new String[],是因为使用SQL语句可能会破坏数据库,即SQL脚本注入


改U

在活动中执行,把Ethan的所有书价格改为9.99

SQLiteDatabase db=dbHelper.getWritableDatabase();
ContentValues values=new ContentValues();
values.put(Cols.PRICE,9.99);
//参数为(表名,ContentValues对象,?是占位符,占位符内容)
db.update("Book",values,Cols.AUTHOR+"=?",new String[]{"Ethan"});


查R

在活动中执行,查找到pages符合67的author和name

SQLiteDatabase db = dbHelper.getWritableDatabase();
//参数为(表名,查询哪些列的信息,约束,占位符内容,需要groupBy的列,对groupBy后的结果进行约束,指定查询结果的排序方式),返回光标
Cursor cursor = db.query("Book", new String[]{Cols.AUTHOR,Cols.NAME}, Cols.PAGES+"=?", new String[]{"67"}, null, null, null);
if (cursor.moveToFirst()) {
    do {
        //此处代码可以放在继承自CursorWrapper类的类中
        String author = cursor.getString(cursor.getColumnIndex(Cols.AUTHOR));
        Log.d("wxc", author);
    } while (cursor.moveToNext());
}
cursor.close();


使用此类,可以完成从数据库中取出数据之后新建实体对象等操作,这个类继承了Cursor的一切,并且可以添加自定义方法;使用时将上文Cursor替换

public class MyCursorWrapper extends CursorWrapper {
    public MyCursorWrapper(Cursor cursor) {
        super(cursor);
    }
        ...
}


事务

事务的特性是保证一系列操作要么全部完成,要么一个都不会完成

SQLiteDatabase db=dbHelper.getWritableDatabase();
            db.beginTransaction();
            try {
                db.delete("Book",null,null);
                ContentValues values=new ContentValues();
                //如果在此处抛出异常,就执行不到setTransactionSuccessful()
                values.put("author", "Ethan");
                values.put("name", "I'm the king");
                values.put("pages", 999);
                values.put("price", 0.1);
                db.insert("Book", null, values);
                db.setTransactionSuccessful();//通知数据库:事务执行成功
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                db.endTransaction();//如果没收到事务执行成功的通知,那么整个事务都不会执行
            }


Glance调试工具

在手机上对应应用生成一个数据库查看器,可以手动修改数据

debugImplementation 'com.guolindev.glance:glance:1.1.0'


最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。

扫码免费领取!!

在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集
在这里插入图片描述
二、源码解析合集

在这里插入图片描述
三、开源框架合集

在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接扫码免费免费领取↓↓↓

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值