简介
持久化是指将内存中的瞬时数据保存成永久的数据(存储到存储设备中),保证在设备断电的时候数据仍然不会丢失。Android设备上常用的持久化技术有以下三类
- 文件存储;
- SharedPreference
- 数据库存储
文件存储:不对内容做任何修改,适合存储简单文本数据或者二进制数据
主要使用了context类中的openFileOutput()、openFileIntput()方法,openFileOutput()接收两个参数:文件名(不要指定路径,路径默认/data/data<packagename>/files/)、文件操作模式(MODE_PRIVATE、MODE_APPEND,前者会覆盖已经存在的文件,后者会在已经存在的文件中追加内容)openFileIntput()接收一个参数:文件名
下面代码中的TextUtils方法可以进行双重判断,如果传进来的字符串为null或者字符串等于空,都会返回true
public class MainActivity extends AppCompatActivity {
private EditText edit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
edit = (EditText) findViewById(R.id.edit);
String inputText = load();
if (!TextUtils.isEmpty(inputText)) {
edit.setText(inputText);
edit.setSelection(inputText.length());
Toast.makeText(this, "Restoring succeeded", Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
String inputText = edit.getText().toString();
save(inputText);
}
public void save(String inputText) {
FileOutputStream out = null;
BufferedWriter writer = null;
try {
out = openFileOutput("data", Context.MODE_PRIVATE);
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(inputText);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public String load() {
FileInputStream in = null;
BufferedReader reader = null;
StringBuilder content = new StringBuilder();
try {
in = openFileInput("data");
reader = new BufferedReader(new InputStreamReader(in));
String line = "";
while ((line = reader.readLine()) != null) {
content.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return content.toString();
}
}
SharedPreference
SharedPreference是一个接口,利用键值对的形式存储数据。
三种方法用于得到SharedPreferences 对象
- Context 类中的getSharedPreferences()方法:接收两个参数,第一个参数用于指定SharedPreferences 文件的名称,如果指定的文件不存在则会创建一个,第二个参数用于指定操作模式,主要有两种模式可以选择,MODE_PRIVATE 和MODE_MULTI_PROCESS(Android6.0中已经被废弃)
- Activity 类中的getPreferences()方法:只接收一个操作模式参数,自动将当前活动的类名作为SharedPreferences 的文件名
- PreferenceManager 类中的getDefaultSharedPreferences()方法:静态方法,接收一个Context 参数,自动使用当前应用程序的包名作为前缀来命名SharedPreferences 文件
数据存储分三步进行:
- 调用SharedPreferences 对象的edit()方法来获取一个SharedPreferences.Editor 对象。
- 向 SharedPreferences.Editor 对象中添加数据,比如添加一个布尔型数据就使用 putBoolean 方法,添加一个字符串则使用 putString()方法,以此类推。
- 调用commit()方法或者apply()方法将添加的数据提交,从而完成数据存储操作。
commit()方法 和apply()方法的区别在于:
- apply没有返回值而commit返回boolean表明修改是否提交成功
- apply是将修改数据原子提交到内存, 而后异步真正提交到硬件磁盘, 而commit是同步的提交到硬件磁盘,因此,在多个并发的提交commit的时候,他们会等待正在处理的commit保存到磁盘后再操作,从而降低了效率。而apply只是原子的提交到内容,后面又调用apply的函数的将会直接覆盖前面的内存数据,这样从一定程度上提高了很多效率。
- apply方法不会提示任何失败的提示。
由于在一个进程中,sharedPreference是单实例,一般不会出现并发冲突,如果对提交的结果不关心的话,建议使用apply,当然需要确保提交成功且有后续操作的话,还是需要用commit的。
用SharedPreference存储账号密码
//拿到一个SharedPreference对象
SharedPreferences sp = getSharedPreferences("config", MODE_PRIVATE);
//拿到编辑器
Editor ed = sp.edit();
//写数据
ed.putString("name", name);
ed.putString ("pass", pass);
//提交
ed.commit();
原理:生成了xml文件,然后保存
从SharedPreference里取数据
SharedPreferences sp = getSharedPreferences("config", MODE_PRIVATE);
//从SharedPreference里取数据
//“”的意思是取不到就返回空字符串
String name = sp.getString ("name", "");
String name = sp.getString ("pass", "");
SQLite数据库(Structured Query Language )
轻量级关系型数据库,遵循了数据库的ACID事物 (原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability))
SQLite数据库 操作数据库要传入上下文
定义一个类继承 SQLiteOpenHelper
public class MyOpenHelper extends SQLiteOpenHelper{
public static final String CREATE_BOOK = "create table Book ("
+ "id integer primary key autoincrement, "
+ "author text, "
+ "price real, "
+ "pages integer, "
+ "name text)";
public static final String CREATE_CATEGORY = "create table Category ("
+ "id integer primary key autoincrement, "
+ "category_name text, "
+ "category_code integer)";
private Context mContext;
//必须定义一个构造函数
//arg0:上写文(数据库在哪个上下文使用)
//arg1:数据库文件的名字
//arg2:游标工厂,游标等同于结果集,使用null表示默认
//arg3:数据库版本,不能小于1,用于升级
public MyOpenHelper(Context context, String name, CursorFactory factory, int version){
super(context, name, factory, version);
mContext = context;
}
//在创建数据库时创建表
@Override
public void onCreate(SQLiteDatabase db) {//数据库被创建时会调用
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {//数据库升级时会调用
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category");
onCreate(db);
}
}
建表语句
create table Book (id integer primary key autoincrement, author text, price real, pages integer, name text)
integer 表示整型, real 表示浮点型,text 表示文本类型,blob 表示二进制类型
使用primary key 将id 列设为主键,并用autoincrement 关键字表示id 列是自增长的
调用SQLiteOpenHelper 的getReadableDatabase()或getWritableDatabase() 方法是可以用于创建和升级数据库的,这两个方法还都会返回一个SQLiteDatabase 对象,借助这个对象就可以对数据进行CRUD 操作了。
- insert()方法它接收三个 参数,第一个参数是表名;第二个 参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL;第三个参数是一个ContentValues 对象,它提供了一系列的put() 方法重载,用于向 ContentValues 中添加数据,只需要将表中的每个列名以及相应的待添加 数据传入即可。
- update()方法,这个方法 接收四个参数,第一个参数表名,在这里指定去更新哪张表里的数 据。第二个参数是 ContentValues 对象,要把更新数据在这里组装进去。第三、第四个参数 用于去约束更新某一行或某几行中的数据,不指定的话默认就是更新所有行。
- delete()方法,这个方法接收三个参数,第一 个参数仍然是表名,第二、第三个参数又是用于去约束删除某一 行或某几行的数据,不指定的话默认就是删除所有行。
- query()方法,调用 query()方法后会返回一个Cursor 对象,查询到的所有数据都将从这个对象中取出。第一个参数是表名,表示我们希望从哪张表中查 询数据。第二个参数用于指定去查询哪几列,如果不指定则默认查询所有列。第三、第四个 参数用于去约束查询某一行或某几行的数据,不指定则默认是查询所有行的数据。第五个参 数用于指定需要去group by 的列,不指定则表示不对查询结果进行group by 操作。第六个参 数用于对group by 之后的数据进行进一步的过滤,不指定则表示不进行过滤。第七个参数用 于指定查询结果的排序方式,不指定则表示使用默认的排序方式。
创建数据库
//创建OpenHelper对象
//获取虚拟上下文:getContext()
MyOpenHelper oh = new MyOpenHelper(getContext(), "person.db", null, 1);
//获得数据库对象,如果数据库不存在,先创建数据库,后获得,如果存在,则直接获得
SQLiteDatabase db = oh.getWritableDatabase();//通过返回的SQLiteDatabase对象可以实现对数据库的增删查改操作
getWritableDatabase();//打开可读写的数据库(在磁盘空间不足时出现异常)
getReadableDatabase();//在磁盘空间不足时打开只读数据库,否则打开可读写数据库
数据库的增删改查
使用API实现增删改查
插入
//以键值对的形式保存要存入数据库的数据
ContentValues cv = new ContentValues();
cv.put("name", "刘能");
cv.put("phone", 1651646);
cv.put("money", 3500);
//返回值是该行的主键,如果出错返回-1;
//第一个参数:表名;第二个参数:在未指定添加数据的情况下给某些可为空的列自动赋值null;第三个参数:ContentValues对象
long i = db.insert("person", null, cv);
删除
//返回值是删除的行数;
//第一个参数:表名;第二个参数:where条件;第三个参数:填充where条件占位符;
int i = db.delete("person", "_id = ? and name = ?", new String[]{"1", "张三"});
修改
ContentValues cv = new ContentValues();
cv.put("money", 25000);
//返回值是修改的行数;
//第一个参数:表名;第二个参数:传入的新值(ContentValues对象);第三个参数:where条件;第四个参数:填充where条件占位符 ;
int i = db.update("person", cv, "name = ?", new String[]{"赵四"});
查询
//arg0:表名
//arg1:要查询的字段
//arg2:查询的where条件
//arg3:填充查询where条件的占位符
Cursor cs = db.query("person", new String[]{"name", "money"}, "name = ?", new String[]{"张三"}, null, null, null);
while(cs.moveToNext()){
//获取指定列的索引值
String name = cs.getString(cs.getColumnIndex("name"));
String money = cs.getString(cs.getColumnIndex("money"));
System.out.println(name + ";" + money);
}
SQL语句
insert into person (name, phone, money) values ('张三', '159874611', 2000);
delete from person where name = '李四' and _id = 4;
update person set money = 6000 where name = '李四';
select name, phone from person where name = '张三';
执行SQL语句实现增删改查
//插入
db.execSQL("insert into person (name, phone, money) values (?, ?, ?);", new Object[]{"张三", 15987461, 75000});
//查找
Cursor cs = db.rawQuery("select _id, name, money from person where name = ?;", new String[]{"张三"});
//把指针移动到下一行,返回值是boolean类型
while(cs.moveToNext){
//先通过列名获取列索引,然后再获取列的内容
String name = cs.getstring(cursor.getColumnIndex("name"));
}
测试方法执行前会调用此方法
protected void setUp() throws Exception {
super.setUp();
//获取虚拟上下文对象
oh = new MyOpenHelper(getContext(), "people.db", null, 1);
}
测试方法执行后会调用此方法
protected void taerDown() throws Exception {
super.taerDown ();
oh.close();
}
事务
保证多条SQL语句要么同时成功,要么同时失败
最常见案例:银行转账
事务api
try {
//开启事务
db.beginTransaction();
ContentValues values = new ContentValues();
values.put("salaty",11111);
db.update("person",values,"name=?",new String[]{"杨松"});
values.clear();
values.put("salaty",22222);
db.update("person",values,"name=?",new String[]{"春晓"});
//设置事务执行成功
db.setTransactionSuccessful();
} finally{
//关闭事务
//如果此时已经设置事务执行成功,则sql语句生效,否则不生效
db.endTransaction();
}
把数据库的数据显示至屏幕
1. 任意插入一些数据
定义业务bean:Person.java
读取数据库的所有数据
Cursor cs = db.query("person", null, null, null, null, null, null);
while(cs.moveToNext()){
String name = cs.getString(cs.getColumnIndex("name"));
String phone = cs.getString(cs.getColumnIndex("phone"));
String money = cs.getString(cs.getColumnIndex("money"));
//把读到的数据封装至Person对象
Person p = new Person(name, phone, money);
//把person对象保存至集合中
people.add(p);
}
分页查询
"0,10":从0开始,总共查询10条
Cursor cs = db.query("person", null, null, null, null, null, null, "0, 10");
分页信息:
1、总共有多少条数据
select count (*)from blacktable;
性能低下:
sql解析器先查询数据库字典,把*转成所有列名和列的类型;
然后把每行数据提取出来,最后统计有多少行数据。
select count (常量)from blacktable; //不需要每行的记录,只需要行数。
2、指定每页显示多少条
需求决定每页行数
3、计算出共有多少页
int pages = (int)(Math.ceil(总量*1.0/每页行数));
4、取每页的数据
select * from blacktable limit 去多少条数据 offset 从哪条数据
select * from blacktable limit 行的起始位置,多少条数据