对于重复或结构化的数据,如联系人信息,将它们保存到数据库是理想选择。这节课假定您熟悉一般的SQL数据库,并帮助您开始在Android上使用SQLite数据库。在Android上,你需要使用数据库的API都在 android.database.sqlite包提供。
SQL数据库的主要原则之一是模式:数据库是如何组织的正式声明。该模式体现在你用来创建数据库的SQL语句中。您可能会发现它有利于创造一个同伴(companion)类,作为合同类为人所知,后者用系统和自文档(self-documentint)的方式明确指定您的模式的布局。
合同类是定义URI、表和列名等常量的一个容器。合同类,允许您在同一个包中的其它所有类里使用相同的常量。这可以让你在一个地方改变某个列名,它就传遍你的代码。
组织合同类的一个好方法就是把你的整个数据库的全局定义放到类的根层次。然后为每个枚举列的表创建一个内部类。
注:通过实现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 beinginstantiated.
private FeedReaderContract() {}
一旦你定义完你的数据库的样子,你应该实现创建和维护数据库和表的方法。下面是一些典型的创建和删除一个表的语句:
private static final String TEXT_TYPE = " TEXT";
private static final String COMMA_SEP = ",";
private static final String SQL_CREATE_ENTRIES =
"CREATETABLE " + 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 String SQL_DELETE_ENTRIES =
"DROPTABLE IF EXISTS " + TABLE_NAME_ENTRIES;
就像您保存在设备内部存储的文件那样,Android的存储你的数据库在与应用程序关联的私有磁盘空间。您的数据是安全的,因为默认情况下,这个区域对其他应用程序是不可访问的。
一组有用的API,可在SQLiteOpenHelper类中找到。当你使用这个类来获取到你的数据库的引用时,系统执行潜在的长时间运行的创建和更新数据库的操作,只会在需要的时候,而不是在应用程序启动时。你所要做的全部就是调用getWritableDatabase()或 getReadableDatabase() 。
注:因为他们可以长时间运行,确保您是在后台线程调用getWritableDatabase()或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为空(empty)框架会为该列插入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方法,你必须传递你想要的列的索引位置,你可以通过调用getColumnIndex() 或getColumnIndexOrThrow()得到列的索引。例如:
cursor.moveToFirst();
long itemId = cursor.getLong(
cursor.getColumnIndexOrThrow(FeedReaderContract.FeedEntry._ID)
);
要删除表中的行,你需要提供的选择条件来确定哪些行。数据库API提供了一种机制来创建可防止SQL注入的选择条件。该机制将选择规范分解成一个选择子句和选择参数。子句定义列,也允许你组合列来试验。参数是试验绑定到子句的值。(原文:The clausedefines the columns to look at, and also allows you to combine column tests.The arguments are values to test against that are bound into the clause.)因为结果与一个常规的SQL语句处理不一样,它对SQL注入是免疫的。
// Define 'where' part of query.
String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
// Specify arguments in placeholder order.
String[] selectionArgs = { String.valueOf(rowId) };
// Issue SQL statement.
db.delete(table_name, selection, selectionArgs);
当你需要修改你的数据库数值的一个子集时,使用 update() 方法。更新表内容结合使用insert() 方法的语法和带where的delete()方法的语法。(原文:Updating the table combines thecontent values syntax of insert()with the where syntax of delete())
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[] selectionArgs = { String.valueOf(rowId) };
int count = db.update(
FeedReaderDbHelper.FeedEntry.TABLE_NAME,
values,
selection,
selectionArgs);