前面說了Android文件的存放區(qū)域,這篇文章將說說數(shù)據(jù)的存放方式之一SQL數(shù)據(jù)庫,這個對于重復或結構化數(shù)據(jù)(比如聯(lián)系人信息)而言是理想之選,使用到的SQL相關API所在的包為android.database.sqlite
Define a Schema and Contract
SQL數(shù)據(jù)庫的主要原則之一是Schema:聲明了數(shù)據(jù)庫是如何組織的. 對于創(chuàng)建類似于Contract類等Companion類很有幫助.
一個Contract類是一個用于存放定義URI名字,表名,列名的常量的容器,通過這個類你可以在同一個包下的其他類中使用這些常量.Android API中的一些Contract類如ContactsContract,CalendarContract,TvContract等等.
組織Contract類的一種良好方法是將對于整個數(shù)據(jù)庫而言是全局性的定義放入該類頂層,然后為每個表表創(chuàng)建內部類。
- 注意: 通過實現(xiàn)BaseColumns接口,你的類就會帶有有個兩個常量_ID和_COUNT,其中_ID對于一些類是有用的,雖然這不是必須的,但是這樣做對于數(shù)據(jù)庫與Android framework的和諧工作是有幫助的.
如下片段,一個Contract類中定義表明和列名:
public final class FeedReaderContract {
// To prevent someone from accidentally instantiating the contract class,
// give it an empty constructor.
public FeedReaderContract() {}
/* Inner class that defines the table contents */
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";
...
}
}
Create a Database Using a SQL Helper
定義了數(shù)據(jù)庫的結構后,就可以來創(chuàng)建和維護數(shù)據(jù)了,下面是創(chuàng)建和刪除Table的語句:
private static final String TEXT_TYPE = " TEXT";
private static final String COMMA_SEP = ",";
private static final String SQL_CREATE_ENTRIES =
"CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +
FeedEntry._ID + " INTEGER PRIMARY KEY," +
FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP +
FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +
... // Any other options for the CREATE command
" )";
private static final String SQL_DELETE_ENTRIES =
"DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;
數(shù)據(jù)庫也是存放在internal storage中,如前文所講,這是安全區(qū)域.
對于Android中操作數(shù)據(jù)庫,有一個類提供了很多有用的API,這個就是SQLiteOpenHelper,里面提供了許多數(shù)據(jù)庫操作方法的回調,你可以override它們然后實現(xiàn)自己的邏輯操作. 同時該類還提供了getWritableDatabase()和getReadableDatabase(),通過這兩個方法可以獲取到SQLiteDatabase實例對象,這樣就可以對數(shù)據(jù)庫進行操作.
- 注意: 數(shù)據(jù)庫操作可能是long-running的,所以要保證對數(shù)據(jù)庫的操作放到異步來進行,比如用AsyncTask或IntentService來操做getWritableDatabase()或getReadableDatabase().
由于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);
}
}
使用時只需實例化一個FeedReaderDbHelper的對象即可:
FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());
Put Information into a Database
使用ContentValues類和insert()方法類插入數(shù)據(jù):
// 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(FeedEntry.COLUMN_NAME_ENTRY_ID, id);
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedEntry.COLUMN_NAME_CONTENT, content);
// Insert the new row, returning the primary key value of the new row
long newRowId;
newRowId = db.insert(
FeedEntry.TABLE_NAME,
FeedEntry.COLUMN_NAME_NULLABLE,
values);
其中insert()的第一個參數(shù)是表名,第二個參數(shù)是當?shù)诙€參數(shù)ContantValues的對象中無數(shù)據(jù)時在相應的列插入NULL的列名.(若第三個參數(shù)為null則不會插入空數(shù)據(jù)).
Read Information from a Database
調用query()方法可以查詢數(shù)據(jù),可以傳一些參數(shù)來篩選,返回的是一個Cursor對象.
SQLiteDatabase db = mDbHelper.getReadableDatabase();
// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
FeedEntry._ID,
FeedEntry.COLUMN_NAME_TITLE,
FeedEntry.COLUMN_NAME_UPDATED,
...
};
// How you want the results sorted in the resulting Cursor
String sortOrder =
FeedEntry.COLUMN_NAME_UPDATED + " DESC";
Cursor c = db.query(
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()方法,來把指針移到結果的第一個entry,也就是整行的數(shù)據(jù),可以通過Cursor的getString(),getLong()等方法來獲取具體值,這些方法需要列的索引,而列的索引可以用 getColumnIndex()或getColumnIndexOrThrow()方法來獲取,如下示例:
cursor.moveToFirst();
long itemId = cursor.getLong(
cursor.getColumnIndexOrThrow(FeedEntry._ID)
);
Delete Information from a Database
刪除數(shù)據(jù)需要指定某行,我們這個數(shù)據(jù)庫的API為創(chuàng)建選擇條件提供了一套機制來防止SQL注入. 這套機制把整個選擇分成了選擇語句和選擇參數(shù),其中選擇語句定義了要查找的列,同時也允許你進行組合測試. 而選擇參數(shù)則是根據(jù)綁定到選擇語句來測試的值,因為這個結果的處理與常規(guī)的SQL語句不同,因此可以防止SQL注入,如下示例:
// Define 'where' part of query.
String selection = 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 a Database
數(shù)據(jù)常規(guī)操作增刪改查中的改,使用update()方法,如下示例:
SQLiteDatabase db = mDbHelper.getReadableDatabase();
// New value for one column
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
// Which row to update, based on the ID
String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
String[] selectionArgs = { String.valueOf(rowId) };
int count = db.update(
FeedReaderDbHelper.FeedEntry.TABLE_NAME,
values,
selection,
selectionArgs);
總結
Android中使用數(shù)據(jù)庫,只需要自定義一個SQLiteOpenHelper的實現(xiàn)類,最好再寫個Contract類將要列名表名等寫成全局靜態(tài)常量,然后在SQLiteOpenHelper中進行相應的操作,除了上面的一些方法,可以直接調用SQLiteDatabase的execSQL相關方法來直接執(zhí)行原生sql語句.
但是對于稍微復雜點的數(shù)據(jù),比如要存放多個類的數(shù)據(jù),那要寫的就比較多,而現(xiàn)在有一些工具和方案可以幫我們簡化數(shù)據(jù)庫的操作,SQLite封裝的工具如:
你可以選擇你覺得好用的作為你的解決方案,還是不錯的.