Android 数据存储
一、文件存储
文件存储是Android中最基本的一种数据存储方式,它不对存储的内容进行任何的格式化处理,所有数据都是原封不动地保存到文件当中的,因而它比较适合用于存储一些简单的文本数据或二进制数据。如果你想使用文件存储的方式来保存一些较为复杂的文本数据,就需要定义一套自己的格式规范,这样方便于之后将数据从文件中重新解析出来。
1.将数据存储到文件中
Context类的openFileOutput ()方法,可以用于将数据存储到指定的文件中。openFileOutput ()方法返回的是一个FileOutputStream对象,得到了这个对象之后就可以使用Java流的方式将数据写入到文件中了。
第一个参数是文件名,不包含路径,默认存储到/data/data/
第二个参数是文件的操作模式。
MODE_PRIVATE是默认的操作模式,表示当指定同样文件名的时候,所写入的内容将会覆盖原文件中的内容。
MODE_APPEND则表示如果该文件已存在就往文件里面追加内容,不存在就创建新文件。
例子:
public void save() {
String data = "Data to save";
FileOutputStream out = null;
BufferedWriter writer = null;
try {
out = openFileOutput("data", Context.MODE_PRIVATE);
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.从文件中读取数据
Context类的openFileInput()方法,只接受一个文件名参数,系统会自动到/data/data/
例子:
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();
}
细节:
edit.setSelection(inputText.length()); //将输入光标移动到文本的末尾位置以便于继续输入。
TextUtils.isEmpty()方法可以一次性判断传入的字符串等于null或者等于空字符串,返回true。
二、SharedPreferences存储
使用键值对来存储数据,支持不同的数据类型存储。使用XML格式来对数据进行管理。
1.将数据存储到SharedPreferences中
首先要获取到SharedPreferences对象。
(1)Context类中的getSharedPreferences()方法
第一个参数指定文件名称,若不存在则创建,,SharedPreferences文件都是存放在/data/data/
(2)Activity类中的getPreferences()方法
只接受模式参数。自动将当前活动的类名作为SharedPreferences的文件名。
(3)PreferenceManager类中的getDefaultSharedPreferences()方法
静态方法,接受context参数,并自动使用当前应用程序的包名作为前缀来命名SharedPreferences文件。
得到SharedPreferences对象后,就可以向SharedPreferences文件中存储数据了。
(1)调用SharedPreferences对象的edit()方法获取一个SharedPreferences.Editor对象
(2)向SharedPreferences.Editor对象中添加数据。例如putString()等。
(3)调用commit()将数据提交。
例子:
SharedPreferences.Editor editor = getSharedPreferences("data",
MODE_PRIVATE).edit();
editor.putString("name", "Tom");
editor.putInt("age", 28);
editor.putBoolean("married", false);
editor.commit();
2.从SharedPreferences中读取数据
每种类型SharedPreferences.Editor都有对应的get方法,第一个参数是键,第二个是默认值,表示当键找不到值时以什么默认值返回。
例子:
SharedPreferences pref = getSharedPreferences("data", MODE_PRIVATE);
String name = pref.getString("name", "");
int age = pref.getInt("age", 0);
boolean married = pref.getBoolean("married", false);
记住密码的例子(修改强制下线项目的代码):
boolean isRemember = pref.getBoolean("remember_password", false);
if (isRemember) {
// 将账号和密码都设置到文本框中
String account = pref.getString("account", "");
String password = pref.getString("password", "");
accountEdit.setText(account);
passwordEdit.setText(password);
rememberPass.setChecked(true);
}
login的点击事件中:
editor = pref.edit();
if (rememberPass.isChecked()) { // 检查复选框是否被选中
editor.putBoolean("remember_password", true);
editor.putString("account", account);
editor.putString("password", password);
} else {
editor.clear();
}
editor.commit();
三、SQLite数据库存储
SQLite是一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少。SQLite不仅支持标准的SQL语法,还遵循了数据库的ACID事务。integer表示整型,real表示浮点型,text表示文本类型,blob表示二进制类型。
1.创建数据库
借助SQLiteOpenHelper抽象帮助类就可以非常简单地对数据库进行创建和升级。
必须创建一个帮助类继承SQLiteOpenHelper类,并重写onCreate()和onUpgrade()方法。构造方法第一个参数是context,第二个是数据库名,第三个允许在查询的时候返回一个自定义的cursor,一般是null,第四个是当前数据库的版本号,用于对数据库进行升级操作。
构建出SQLiteOpenHelper实例后再调用getReadableDatabase()或getWritableDatabase()方法就能够创建数据库了,数据库文件会存放在/data/data/
注:getReadableDatabase()和getWritableDatabase()。这两个方法都可以创建或打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。不同的是,当数据库不可写入的时候(如磁盘空间已满)getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,而getWritableDatabase()方法则将出现异常。
例子:
public class MyDatabaseHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "create table book ("
+ "id integer primary key autoincrement, "
+ "author text, "
+ "price real, "
+ "pages integer, "
+ "name text)";
private Context mContext;
public MyDatabaseHelper(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);
Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
MainActivity中:private MyDatabaseHelper dbHelper;
dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1);
点击事件中:
dbHelper.getWritableDatabase();
2.升级数据库
例子:若要新增表Category。
public static final String CREATE_CATEGORY = "create table Category ("
+ "id integer primary key autoincrement, "
+ "category_name text, "
+ "category_code integer)";
在onCreate()方法中增加:
db.execSQL(CREATE_CATEGORY);
在onUpgrade()方法中增加:
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category");
onCreate(db);
MainActivity中修改:
dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);
3.添加数据
调用SQLiteOpenHelper的getReadableDatabase()或getWritableDatabase()方法是可以用于创建和升级数据库的,不仅如此,这两个方法还都会返回一个SQLiteDatabase对象,借助这个对象就可以对数据进行CRUD操作了。
SQLiteDatabase中提供了一个insert()方法,这个方法就是专门用于添加数据的。它接收三个参数,第一个参数是表名,第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般直接传入null即可。第三个参数是一个ContentValues对象,它提供了一系列的put()方法重载,用于向ContentValues中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可。
例子:
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
// 开始组装第一条数据
values.put("name", "The Da Vinci Code");
values.put("author", "Dan Brown");
values.put("pages", 454);
values.put("price", 16.96);
db.insert("Book", null, values); // 插入第一条数据
values.clear();
// 开始组装第二条数据
values.put("name", "The Lost Symbol");
values.put("author", "Dan Brown");
values.put("pages", 510);
values.put("price", 19.95);
db.insert("Book", null, values); // 插入第二条数据
4.更新数据
update()方法用于对数据进行更新,这个方法接收四个参数,第一个参数是表名,第二个参数是ContentValues对象,要把更新数据在这里组装进去。第三个参数对应的是SQL语句的where部分,表示去更新所有name等于?的行,而?是一个占位符,可以通过第四个参数提供的一个字符串数组为第三个参数中的每个占位符指定相应的内容。不指定第三、四个参数的话默认就是更新所有行。
例子:
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price", 10.99);
db.update("Book", values, "name = ?", new String[] { "The Da Vinci Code" });
5.删除数据
delete()方法专门用于删除数据,这个方法接收三个参数,第一个参数是表名,第二、第三个参数用于去约束删除某一行或某几行的数据,不指定的话默认就是删除所有行。
例子:
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete("Book", "pages > ?", new String[] { "500" });
6.查询数据
query()方法第一个参数是表名,第二个参数用于指定去查询哪几列,如果不指定则默认查询所有列。第三、第四个参数用于去约束查询某一行或某几行的数据,不指定则默认是查询所有行的数据。第五个参数用于指定需要去group by的列,不指定则表示不对查询结果进行group by操作。第六个参数用于对group by之后的数据进行进一步的过滤,不指定则表示不进行过滤。第七个参数用于指定查询结果的排序方式,不指定则表示使用默认的排序方式。
query()方法参数 |
对应SQL部分 |
描述 |
table |
from table_name |
指定查询的表名 |
columns |
select column1, column2 |
指定查询的列名 |
selection |
where column = value |
指定where的约束条件 |
selectionArgs |
- |
为where中的占位符提供具体的值 |
groupBy |
group by column |
指定需要group by的列 |
having |
having column = value |
对group by后的结果进一步约束 |
orderBy |
order by column1, column2 |
指定查询结果的排序方式 |
多数情况下只需要传入少数几个参数就可以完成查询操作了。调用query()方法后会返回一个Cursor对象,查询到的所有数据都将从这个对象中取出。
例子:
SQLiteDatabase db = dbHelper.getWritableDatabase();
// 查询Book表中所有的数据
Cursor cursor = db.query("Book", null, null, null, null, null, null);
if (cursor.moveToFirst()) {
do {
// 遍历Cursor对象,取出数据并打印
String name = cursor.getString(cursor. getColumnIndex("name"));
String author = cursor.getString(cursor. getColumnIndex("author"));
int pages = cursor.getInt(cursor.getColumnIndex ("pages"));
double price = cursor.getDouble(cursor. getColumnIndex("price"));
} while (cursor.moveToNext());
}
cursor.close();
7.用SQL语句操作数据库
添加数据的方法如下:
db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)",
new String[] { "The Da Vinci Code", "Dan Brown", "454", "16.96" });
db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)",
new String[] { "The Lost Symbol", "Dan Brown", "510", "19.95" });
更新数据的方法如下:
db.execSQL("update Book set price = ? where name = ?", new String[] { "10.99", "The Da Vinci Code" });
删除数据的方法如下:
db.execSQL("delete from Book where pages > ?", new String[] { "500" });
查询数据的方法如下:
db.rawQuery("select * from Book", null);
四、SQLite数据库实践
1.使用事务
SQLite数据库是支持事务的,事务的特性可以保证让某一系列的操作要么全部完成,要么一个都不会完成。
例子:
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.beginTransaction(); // 开启事务
try {
db.delete("Book", null, null);
ContentValues values = new ContentValues();
values.put("name", "Game of Thrones");
values.put("author", "George Martin");
values.put("pages", 720);
values.put("price", 20.85);
db.insert("Book", null, values);
db.setTransactionSuccessful(); // 事务已经执行成功
} catch (Exception e) {
e.printStackTrace();
} finally {
db.endTransaction(); // 结束事务
}
}
});
2.升级数据库最佳写法
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
switch (oldVersion) {
case 1:
db.execSQL(CREATE_CATEGORY);
case 2:
db.execSQL("alter table Book add column category_id integer");
default:
}
将每一版本的更新放在switch语句中。
switch中每一个case的最后都是没有使用break的,为了保证在跨版本升级的时候,每一次的数据库修改都能被全部执行到。比如用户当前是从第二版程序升级到第三版程序的,那么case 2中的逻辑就会执行。而如果用户是直接从第一版程序升级到第三版程序的,那么case 1和case 2中的逻辑都会执行。使用这种方式来维护数据库的升级,不管版本怎样更新,都可以保证数据库的表结构是最新的,而且表中的数据也完全不会丢失了。