SQLite数据库
应用沙盒目录
/data/data/com.example.criminalintent
SQLite的特点
- SQLite使用单个文件存储数据,使用SQLite库读取数据。
- 创建表字段时,不需要指定字段类型。
定义Schema
创建com.example.criminalintent.database.CrimeDbSchema类,定义数据表:
public class CrimeDbSchema {
//数据表
public static final class CrimeTable {
//表名
public static final String NAME = "crimes";
//表的字段
public static final class Cols {
public static final String UUID = "uuid";
public static final String TITLE = "title";
public static final String DATE = "date";
public static final String SOLVED = "solved";
}
}
}
创建初始数据库
创建数据库时,建议总是遵循以下步骤。
-
确认目标数据库是否存在。
-
如果不存在,首先创建数据库,然后创建数据库表以及必需的初始化数据。
-
如果存在,打开并确认CrimeDbSchema是否是最新版本。
-
如果是旧版本,就运行相关代码升级到最新版本。
Android提供的 SQLiteOpenHelper 类可以帮我们处理这些。在数据库包中创
建 CrimeBaseHelper 类:
public class CrimeBaseHelper extends SQLiteOpenHelper {
private static final int VERSION = 1;
private static final String DATABASE_NAME = "crimeBase.db";
public CrimeBaseHelper(Context context) {
super(context, DATABASE_NAME, null, VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
//执行创建数据表的SQL语句
db.execSQL("create table " + CrimeTable.NAME +
"(" + " _id integer primary key autoincrement, " +
CrimeTable.Cols.UUID + ", " +
CrimeTable.Cols.TITLE + ", " +
CrimeTable.Cols.DATE + ", " +
CrimeTable.Cols.SOLVED + ")"
);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { }
}
在CrimeLab中用CrimeBaseHelper创建crime数据库:
public class CrimeLab {
private static CrimeLab sCrimeLab;
private Context mContext;
private SQLiteDatabase mDatabase;
private CrimeLab(Context context) {
mContext = context.getApplicationContext();
mDatabase = new CrimeBaseHelper(mContext).getWritableDatabase(); //创建/打开数据库
}
}
写入数据库
使用ContentValues
CrimeLab.java
//根据crime创建ContentValues并返回
private static ContentValues getContentValues(Crime crime) {
ContentValues values = new ContentValues();
values.put(CrimeTable.Cols.UUID, crime.getId().toString());
values.put(CrimeTable.Cols.TITLE, crime.getTitle());
values.put(CrimeTable.Cols.DATE, crime.getDate().getTime());
values.put(CrimeTable.Cols.SOLVED, crime.isSolved() ? 1 : 0);
return values;
}
插入记录
CrimeLab.java
//添加新的陋习
public void addCrime(Crime crime) {
ContentValues values = getContentValues(crime);
/**
* 向数据库表中插入记录
* @param table String 数据库表名
* @param nullColumnHack String
* @param values ContentValues 要写入的数据
* @return long
*/
mDatabase.insert(CrimeTable.NAME, null, values);
}
更新记录
CrimeLab.java
//更新陋习
public void updateCrime(Crime crime) {
String uuidString = crime.getId().toString();
ContentValues values = getContentValues(crime);
/**
* 更新数据库表中的记录
* @param table String
* @param values ContentValues
* @param whereClause String
* @param whereArgs String[]
* @return int
*/
mDatabase.update(CrimeTable.NAME, values, CrimeTable.Cols.UUID + " = ?", new String[] { uuidString });
}
更新Crime数据
在CrimeFragment中修改Crime实例后,通过覆盖CrimeFragment.onPause()方法完成更新:
CrimeFragment.java
@Override
public void onPause() {
super.onPause();
//修改crime内容后需要更新
CrimeLab.get(getActivity()).updateCrime(mCrime);
}
查询记录
CrimeLab.java
//查询陋习
private CrimeCursorWrapper queryCrimes(String whereClause, String[] whereArgs) {
/**
* 查询数据库表中的记录
* @param table
* @param columns
* @param selection
* @param selectionArgs
* @param groupBy
* @param having
* @param orderBy
* @return Cursor
*/
Cursor cursor = mDatabase.query(
CrimeTable.NAME,
null,
whereClause,
whereArgs,
null,
null,
null
);
return new CrimeCursorWrapper(cursor);
}
使用CursorWrapper
cursor是个表数据处理工具,其任务是封装数据表中的原始字段值。
新增Crime类的构造方法
public Crime() {
this(UUID.randomUUID());
}
public Crime(UUID id) {
mId = id;
mDate = new Date();
}
新建CrimeCursorWrapper类
public class CrimeCursorWrapper extends CursorWrapper {
public CrimeCursorWrapper(Cursor cursor) {
super(cursor);
}
public Crime getCrime() {
String uuidString = getString(getColumnIndex(CrimeTable.Cols.UUID));
String title = getString(getColumnIndex(CrimeTable.Cols.TITLE));
long date = getLong(getColumnIndex(CrimeTable.Cols.DATE));
int isSolved = getInt(getColumnIndex(CrimeTable.Cols.SOLVED));
Crime crime = new Crime(UUID.fromString(uuidString));
crime.setTitle(title);
crime.setDate(new Date(date));
crime.setSolved(isSolved != 0);
return crime;
}
}
返回Crime列表
CrimeLab.java
//获得所有陋习
public List<Crime> getCrimes() {
List<Crime> crimes = new ArrayList<>();
CrimeCursorWrapper cursor = queryCrimes(null, null);
try {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
crimes.add(cursor.getCrime());
cursor.moveToNext();
}
}
finally {
cursor.close();
}
return crimes;
}
返回指定ID的Crime
CrimeLab.java
//获得指定ID的陋习
public Crime getCrime(UUID id) {
CrimeCursorWrapper cursor = queryCrimes(
CrimeTable.Cols.UUID + " = ?",
new String[] { id.toString() }
);
try {
if (cursor.getCount() == 0) return null;
cursor.moveToFirst();
return cursor.getCrime();
}
finally {
cursor.close();
}
}
更新模型层数据
虽然 Cirme 记录已存入数据库,但数据读取还未完善,编辑完新的crime后,尝试点击回退按钮,CrimePagerActivity 并没有相应刷新。
这是因为 CrimeLab 的工作方式已经改变。以前,只有一个 List<Crime> ,而且每个 Crime在 List<Crime> 中只存有一个对象,要获取哪个 Crime 只能去找 mCrimes 。现在,mCrimes 已废弃不用了。因此,getCrimes() 方法返回的 List<Crime> 是 Crime 对象的快照。要刷新 CrimeListActivity 界面,首先要更新这个快照。
CrimeListFragment.CrimeAdapter
public void setCrimes(List<Crime> crimes) {
mCrimes = crimes;
}
然后在updateUI()方法中调用setCrimes(List<Crime>)方法:
CrimeListFragment.java
//为RecyclerView设置Adapter
private void updateUI() {
CrimeLab crimeLab = CrimeLab.get(getActivity());
List<Crime> crimes = crimeLab.getCrimes();
if (mAdapter == null) {
mAdapter = new CrimeAdapter(crimes);
mCrimeRecyclerView.setAdapter(mAdapter);
}
else {
mAdapter.setCrimes(crimes);
mAdapter.notifyDataSetChanged();
}
//设置工具栏子标题
updateSubtitle();
}