保存数据到数据库中是理想的重复或者结构化数据的方式。例如联系人信息。这节课程假设你熟悉通常的SQL数据库,并帮助你开始在Android中的SQLite数据库。在android.database.sqlite包中的API,你将需要使用一个在Android平台有效的数据库。
定义一个模式和契约
—————————————————————————————————————————————————————————————————
SQL数据库一个主要原则是这个模式:数据库是如何被组织的一个正式说明。这个模式被映射到你用来创建你的数据库的SQL语句。你会发现它有助于创建一个相关类,被称为一个契约类,它明确指定了你的模式在系统和自定义方式中的布局。
一个契约类是一个常量容器,它定义了URIs的名字,表,和列。这个常量类允许你在同样的包中的所有其它类使用同样的常量。这让你在一个地方改变列的名称和通过你的代码有它的声明。
组织一个契约类的好的方式是使定义对于你整个数据根级别的类是全局的。然后为每个列举它的列的表创建一个内部类。
注意:通过实现BaseColumns接口,你的内部类能继承一个被称为_ID私有的键域,一些Android类如游标适配器将会期望拥有它。它不是必须的,但是它能帮助你的数据库在Android框架中和谐的工作。
例如,这个代码块定义了一个单独表的表名和列名:
public static abstract class FeedEntry implements BaseColumns {
public static final String TABLE_NAME = "entry";
public static final String COLUMN_NAME_ENTRY_ID = "entryid";
public static final String COLUMN_NAME_TITLE = "title";
public static final String COLUMN_NAME_SUBTITLE = "subtitle";
...
}
为了阻止其它人无意的实例化这个契约类,给它一个空的构造器。
// Prevents the FeedReaderContract class from being instantiated.
private FeedReaderContract() {}
使用一个SQLHelper类创建一个数据库
——————————————————————————————————————————————
一旦你定义了你的数据的外貌,你应该实现创建和维护数据库和表的方法。下面是一些典型的声明,创建和删除一个表:
private static final String TEXT_TYPE = " TEXT";
private static final String COMMA_SEP = ",";
private static final String SQL_CREATE_ENTRIES =
"CREATE TABLE " + FeedReaderContract.FeedEntry.TABLE_NAME + " (" +
FeedReaderContract.FeedEntry._ID + " INTEGER PRIMARY KEY," +
FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP +
FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +
... // Any other options for the CREATE command
" )";
private static final Str_DELETE_ENTRIES =
"DROP TABLE IF EXISTS " + TABLE_NAME_ENTRIES;
正如你保存在设备的内部存储的文件,Andrid保存你的数据库在一个和应用程序相关的私有的硬盘空间。你的数据是安全的,因为默认情况下这个空间对于其它应用程序是不能访问的。
在SQLiteOpenHelter类中有一个可利用的API集合。当你使用这个类来获取你的数据库引用的时候,仅仅当需要和不是应用程序启动的时候,系统执行执行隐藏的长时间操作来创建和更新数据库。所有你需要做的是调用getWritableDatabase()方法或者getReadableDatabase()方法。
注意:因为他们长时间运行,确保你在后台线程调用getWritableDtabase()方法或者getReadableDatabase()方法,例如使用AsyncTask或者IntentService。
为了使用SQLiteOpenHelper,创建一个子类,它覆盖了onCreate()方法,onUpgrade()方法和onOpen()回调方法。你也可能想实现onDowngrade()方法,但是它不是必须的。
例如,这里是SQLiteOpenHelper的一个实现,它使用了上面显示的一些命令:
public class FeedReaderDbHelper extends SQLiteOpenHelper {
// If you change the database schema, you must increment the database version.
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "FeedReader.db";
public FeedReaderDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL_CREATE_ENTRIES);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// This database is only a cache for online data, so its upgrade policy is
// to simply to discard the data and start over
db.execSQL(SQL_DELETE_ENTRIES);
onCreate(db);
}
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
onUpgrade(db, oldVersion, newVersion);
}
}
为了访问你的数据库,实例化你的SQLiteOpenHelper子类:
FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());
将信息保存到一个数据库中
————————————————————————————————————————————————————————————————
通过传递一个ContentValues对象给insert()方法来向数据库中插入数据:
// Gets the data repository in write mode
SQLiteDatabase db = mDbHelper.getWritableDatabase();
// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID, id);
values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_CONTENT, content);
// Insert the new row, returning the primary key value of the new row
long newRowId;
newRowId = db.insert(
FeedReaderContract.FeedEntry.TABLE_NAME,
FeedReaderContract.FeedEntry.COLUMN_NAME_NULLABLE,
values);
inSert()方法的第一个参数表名。第二个参数提供了一个列的名称,在ContentValues是空的情况下,框架可以插入NULL(如果你替代设置这个值为“null”,那么框架将不会插入一行,当没有数据的时候)。
从一个数据库中读取数据
——————————————————————————————————————————————
为了读取一个数据库,使用query()方法,给它传递你的选择条件和期望的列。这个方法联合insert()和update()方法的元素,除了定了你想获取的数据的列列表,而不是插入的数据。查询返回给你的结果是一个Cursor对象。
SQLiteDatabase db = mDbHelper.getReadableDatabase();
// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
FeedReaderContract.FeedEntry._ID,
FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE,
FeedReaderContract.FeedEntry.COLUMN_NAME_UPDATED,
...
};
// How you want the results sorted in the resulting Cursor
String sortOrder =
FeedReaderContract.FeedEntry.COLUMN_NAME_UPDATED + " DESC";
Cursor c = db.query(
FeedReaderContract.FeedEntry.TABLE_NAME, // The table to query
projection, // The columns to return
selection, // The columns for the WHERE clause
selectionArgs, // The values for the WHERE clause
null, // don't group the rows
null, // don't filter by row groups
sortOrder // The sort order
);
为了查看在这个游标中的行,使用Cursor的一个移动方法,在你开始读取值之前,你必须总是调用它。通常的,你应该通过调用moveToFirst()方法开始,它定位“读位置”,在结果的第一个实例。对于每行,你能通过调用Cursor对象的一个get方法来读取一个列的值,例如getString()方法或者getLong()方法。对于每个get方法,你必须传递你期望的列的索引位置,它可以通过调用getColunIndex()或者getColunIndexOrThrow()方法获取。例如:
cursor.moveToFirst();
long itemId = cursor.getLong(
cursor.getColumnIndexOrThrow(FeedReaderContract.FeedEntry._ID)
);
从一个数据库中删除数据
——————————————————————————————————————————————
为了从一个表中删除行,你需要提供识别行的选择条件。数据库API提供了一种机制来创建选择条件,保护违反SQL注入。这个机制分配选择规格到一个选择子句和选择参数。子句定义了查看的列,并且也允许你联合列测试。参数测试违反,它被绑定到子句中。因为结果和一个通常的SQL语句处理不同,它避免了SQL注入。
// Define 'where' part of query.
String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
// Specify arguments in placeholder order.
String[] selelectionArgs = { String.valueOf(rowId) };
// Issue SQL statement.
db.delete(table_name, selection, selectionArgs);
更新一个数据库
——————————————————————————————————————————————
当你需要修改你的数据库的一直子集的值的时候,使用update()方法。
更新表联合insert()值语法格式和delete()where语法格式。
SQLiteDatabase db = mDbHelper.getReadableDatabase();
// New value for one column
ContentValues values = new ContentValues();
values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, title);
// Which row to update, based on the ID
String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
String[] selelectionArgs = { String.valueOf(rowId) };
int count = db.update(
FeedReaderDbHelper.FeedEntry.TABLE_NAME,
values,
selection,
selectionArgs);