数据持久化是开发过程中不可避免的,因为数据是核心,是非常重要的。
总结一下常用的数据持久化的方式:
- SharedPreferences存储数据
- 文件存储数据
- SQLite数据库存储数据
- ContentProvider存储数据
话不多说,上干货。
适用范围:
保存少量的数据,且这些数据的格式非常简单(基本数据类型
)。比如应用程序的各种配置信息(如是否打开音效、是否使用震动效果、小游戏的玩家积分等),解锁口令密码等
核心原理:
数据的保存形式是基于XML文件存储的key-value键值对,通常用来存储一些简单的配置信息。通过DDMS的File Explorer面板,展开文件浏览树,很明显SharedPreferences数据总是存储在/data/data//shared_prefs目录下。SharedPreferences对象本身只能获取数据而不支持存储和修改,存储和修改是通过SharedPreferences.edit()获取的嵌入类Editor实现。
SharedPreferences sp = getSharedPreferences(String name, int mode);
参数:
name: 首选项文件的名称
mode: 首选项文件打开模式
MODE_PRIVATE 只能在本应用程序读写
MODE_WORLD_READABLE 能被其他应用读
MODE_WORLD_WRITEABLE 能被其他应用读写
//获取嵌入类Editor
SharedPreferences.Editor edit = sp.edit();
//写数据
edit.putXXXX(String key, value);
//提交
edit.commit();
切记,切记,写完数据之后一定要提交,否则首选项文件不会保存数据
来看一下官方开发文档中的介绍:
简单翻译一下:
提交从编辑首选项对象的编辑器返回的首选项文件中的改变(大体就是这个意思,若是翻译的不好,大佬勿喷
)
翻译过来有点绕,断一下句好理解一些了:
提交 / 从 / 编辑首选项对象的编辑器 / 返回的 / 首选项文件中的改变
不提交就不会保存数据的更改
SharedPreferences.getXXXX(String key, defaultvalue);
layout布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
tools:context=".MainActivity">
<Button
android:id="@+id/msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="信息" />
</LinearLayout>
核心代码:
/*
* 获得 共享首选项对象(p1:文件名,p2:读写模式)
* 只有当前程序对test进行读写(test不存在时会自动创建)
*/
SharedPreferences sp = getSharedPreferences("test", MODE_PRIVATE);
/*
* 写数据,通过嵌套类Editor写数据
* 写数据,保存在内存中没有加入磁盘中
*/
SharedPreferences.Editor edit = sp.edit();
edit.putString("姓名", "张三");
edit.putInt("年龄", 21);
edit.putBoolean("性别", true);
//提交数据
edit.commit();
//读取数据
String str = "姓名:" + sp.getString("姓名", "") +
",年龄:" + sp.getInt("年龄", 0) +
",性别:" + sp.getBoolean("性别", true);
Button msgBtn = findViewById(R.id.msg);
msgBtn.setOnClickListener((v) -> {
Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
});
效果图:
核心代码
FileOutputStream fos = openFileOutput(fileName, mode);
fos.write(message.getBytes());
fos.close();
参数:
fileName: 数据待写入的文件名称
mode:文件打开模式
MODE_PRIVATE: 为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把
新写入的内容追加到原文件中。可以使用MODE_APPEND
MODE_APPEND: 模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
MODE_WORLD_READABLE: 表示当前文件可以被其他应用读取
MODE_WORLD_WRITEABLE: 表示当前文件可以被其他应用写入
核心代码
//fileName为待读取数据的文件的名称
FileInputStream inStream = openFileInput(fileName);
byte[] buffer = new byte[1024];
int len = 0;
StringBuilder sb = new StringBuilder();
//从文件中读取数据
while ((len = inStream.read(buffer)) != -1) {
sb.append(new String(buffer, 0, len));
}
//关闭输入流
inStream.close();
//转化为字符串
str = sb.toString();
/**
* 读出文件中的数据
*
* @param fileName 文件的名称
* @return 读出的数据
*/
public String read(String fileName) {
try {
//创建一个FileInputStream对象
FileInputStream inStream = openFileInput(fileName);
byte[] buffer = new byte[1024];
int len = 0;
StringBuilder sb = new StringBuilder();
//从文件中读取数据
while ((len = inStream.read(buffer)) != -1) {
sb.append(new String(buffer, 0, len));
}
//关闭输入流
inStream.close();
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 向文件中写入数据
*
* @param message 待写入的数据
* @param fileName 文件名称
* @param mode 打开文件的模式
*/
public void write(String message, String fileName, int mode) {
//获取输入值
if (message == null)
return;
try {
//创建一个FileOutputStream对象,mode为文件打开的模式
FileOutputStream fos = openFileOutput(fileName, mode);
//将获取过来的值放入文件
fos.write(message.getBytes());
//关闭输出流
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
layout布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="信息" />
<Button
android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="商店" />
</LinearLayout>
MainActivity主要代码(实际开发中不能这么写,只作为演示用
)
write("篮球:10个\n排球:5个\n羽毛球:6个\n", "message.txt", MODE_PRIVATE);
Button info = findViewById(R.id.info);
info.setOnClickListener((v) -> {
String read = read("message.txt");
Toast.makeText(MainActivity.this, read, Toast.LENGTH_SHORT).show();
});
效果图:
SQLite是轻量级嵌入式数据库引擎,它支持SQL语言,并且只利用很少的内存就有很好的性能。在我们为移动设备开发应用程序时,也许就要使用到SQLite,所以我们就需要掌握移动设备上的SQLite开发技巧。
使用SQLite数据库需要自定义一个类继承SQLiteOpenHelper帮助类
来创建和升级数据库。
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;
public class MyHelper extends SQLiteOpenHelper {
Context myContext;//上下文对象
/**
* @param context 上下文对象
* @param name 数据库名称
* @param factory 游标工具类,用null
* @param version 数据库版本,整数
*/
public MyHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
myContext = context;
}
//第一次创建数据库时调用,如果数据库已经存在,就不在调用
@Override
public void onCreate(SQLiteDatabase db) {
//创建一个学生表吧
String createTable = "create table student ("
+ "id integer primary key autoincrement,"
+ "sNumber text,"
+ "sName text,"
+ "sAge text)";
db.execSQL(createTable);//创建数据库表
Toast.makeText(myContext, "学生表创建完毕!", Toast.LENGTH_LONG).show();
}
//数据库版本变化时,会调用onUpgrade()
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//你的代码
}
}
使用数据库类SQLiteDatabase
对数据库进行CRUD操作
伪代码如下:
MyHelper helper = new MyHelper(MainActivity.this, "stud.db", null, 1);
SQLiteDatabase database = helper.getWritableDatabase();
//增 在数据库例添加几条数据
ContentValues values = new ContentValues();//ContentValue:封装表中一行,用来添加或修改数据表
cv.put("sNumber","2018001");
cv.put("sName", "Tom");
cv.put("sAge", "21");
database.insert("student", null, cv);
cv.clear();
cv.put("sNumber","2018002");
cv.put("sName", "Tom");
cv.put("sAge", "22");
database.insert("student", null, cv);
cv.clear();
cv.put("sNumber","2018003");
cv.put("sName", "Tom");
cv.put("sAge", "19");
database.insert("student", null, cv);
cv.clear();
cv.put("sNumber","2018004");
cv.put("sName", "Jack");
cv.put("sAge", "21");
database.insert("student", null, cv);
cv.clear();
//删 删除名字叫“Tom”,年龄为22岁的数据
int delete = database.delete("student", "sName = ? and sAge = ?", new String[]{"Tom", "22"});
System.out.println("res = " + delete);
//改 修改名字叫“Tom”,年龄为21岁的数据,把名字修改为“Tom2”
values.put("sName", "Tom2");//修改的数据
int update = database.update("student", values, "SName = ? and sAge = ?", new String[]{"Tom", "21"});
cv.clear();
System.out.println("res = " + update);
//查 查询所有数据
Cursor stu = database.query("student", null, null, null, null, null, null);//Cursor:游标类,封装查询结果
if (stu.moveToFirst()) {
do {
String sNumber = stu.getString(stu.getColumnIndex("sNumber"));
String sName = stu.getString(stu.getColumnIndex("sName"));
String sAge = stu.getString(stu.getColumnIndex("sAge"));
System.out.println("id:" + sNumber + "\t,name:" + sName + "\t,age:" + sAge);
} while (stu.moveToNext());
} else {
System.out.println("没找到");
}
内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,不同于文件存储和SharedPreferences存储中的两种全局可读写操作模式,内容提供器可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄漏的风险
详细内容请参考文章《内容提供器ContentProvider》