在下一節,我們將探討第12章給出的和視頻相關的元數據信息的持久性存儲:標題、描述和視頻的URL。這塊代碼駐留在Android的內容提供者中,這對於數據庫代碼是比較合適的。在這裡將不詳細說明內容提供者,而將探討如何編寫數據庫。第12章將詳細說明如何實現內容提供者。後面的代碼將幫助說明在Android中如何創建和使用SQLite。這個應用將通過sqlite3命令行工具使用和之前相同的數據庫。但是,這次我們將編寫代碼,使用Android API來操作數據。
SimpleVideoDbHelper類的基礎結構
在這個例子中,文件SimpleFinchVideoContentProvider.java封裝了所有必要的SQL邏輯,其能夠在Android的simple_video數據庫中正常工作。需要在該數據庫中訪問持久性數據的應用和它提供的供應商及游標交互,第12章將解釋這些內容。把數據真正是如何存儲的詳細信息完全在客戶端屏蔽。這是良好的編程習慣,應該在所有的使用數據庫的Android應用中這樣做。
現在,因為我們的重點是在Android中如何使用數據庫,瞭解SimpleVideoDbHelper是provider中的數據庫模型:所有和數據庫實現相關的,如數據庫名、列名、表的定義,在類中都起作用。當然,對於大型、複雜的數據庫,helper類可能要複雜得多,有多個組件組成。
SimpleVideoDbHelper類繼承自抽像類SQLiteOpenHelper,因此必須重寫onCreate方法和onUpgrade方法。當應用第一次啟動時會自動調用onCreate方法,其任務是要創建數據庫。當提交新版本的應用時,可能需要更新數據庫,可能增加表、增加列甚至是完全修改模式。當必要的時候,任務會落到onUpgrade方法上,每當構造函數調用的DATABASE_VERSION版本和保存在數據庫中的版本不同時就會調用onUpgrade方法。當提交新版本的數據庫時,必須對其版本號執行遞增操作:
public static final String VIDEO_TABLE_NAME = \"video\"; public static final String DATABASE_NAME = SIMPLE_VIDEO + \".db\"; private static int DATABASE_VERSION = 2; public static final int ID_COLUMN = 0; public static final int TITLE_COLUMN = 1; public static final int DESCRIPTION_COLUMN = 2; public static final int TIMESTAMP_COLUMN = 3; public static final int QUERY_TEXT_COLUMN = 4; public static final int MEDIA_ID_COLUMN = 5; private static class SimpleVideoDbHelper extends SQLiteOpenHelper { private SimpleVideoDbHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) { super(context, name, factory, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase sqLiteDatabase) { createTable(sqLiteDatabase); } private void createTable(SQLiteDatabase sqLiteDatabase) { String qs = \"CREATE TABLE \" + VIDEO_TABLE_NAME + \" (\" + FinchVideo.SimpleVideos._ID + \" INTEGER PRIMARY KEY AUTOINCREMENT, \" + FinchVideo.SimpleVideos.TITLE_NAME + \" TEXT, \" + FinchVideo.SimpleVideos.DESCRIPTION_NAME + \" TEXT, \" + FinchVideo.SimpleVideos.URI_NAME + \" TEXT);\"; sqLiteDatabase.execSQL(qs); } @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldv, int newv) { sqLiteDatabase.execSQL(\"DROP TABLE IF EXISTS \" + VIDEO_TABLE_NAME + \";\"); createTable(sqLiteDatabase); } }
和SimpleVideoDbHelper代碼相關的基本元素是:
常量
SimpleVideoDbHelper類定義了四個重要的常量:
DATABASE_NAME
它保存了數據庫的文件名稱,如這個例子中是simple_video.db。它對實際的SQLite數據庫文件進行命名。我們之前提到過,該文件保存在路徑/data/data/com.oreilly.demo.pa.finchvideo/databases/simple_video.db中,Android會創建該數據庫文件。
DATABASE_VERSION
它定義了數據庫版本,當修改數據庫模式時,可以任意選擇和增加該版本號。如果機器的數據庫版本號小於DATABASE_VERSION,系統會運行onUpgrade方法,把數據庫升級到當前版本。
VIDEO_TABLE_NAME
這是在這個簡單的數據庫中唯一的表名。
*_NAME
這些是數據庫的列名。正如前面提到的,為通過游標訪問的任何表定義一個名為_id的字段並把它作為主鍵是必須的。
構造函數
該provider的數據庫構造函數SimpleVideoDbHelper使用super函數調用其父類的構造函數。父類的構造函數執行了創建數據庫對象的大部分操作。
onCreate
當Android應用嘗試從數據庫中讀取數據或寫入數據,而該數據庫不存在時,Android框架會執行onCreate方法。在YouTubeDbHelper類中的onCreate方法顯示了創建數據庫的一種方式。如果初始化數據庫需要大量的SQL代碼,最好把這些代碼保存在resource文件strings.xml中。這會增強Java代碼的可讀性。但是它也使得開發人員在修改代碼時需要查看兩個不同的文件。當然,如果程序使用的數據庫很簡單,如在SimpleVideoDbHelper中所執行的,則直接在Java代碼中編寫SQL可能更簡單,或者如果使用查詢生成器,則可能都不需要SQL。
注意:如果想從String resource中加載SQL,必須注意在Android文檔中所描述的string的變化:在resource字符串中通過反斜槓escape所有單引號和雙引號(把\"改成\",\'改成\'),使用屬性formatted=\"false\"。例如:
<string name=\"sql_query\" formatted=\"false\"> SELECT * FROM videos WHERE name LIKE \"%cycle%\" </string>
onCreate方法實際上並不需要創建數據庫。傳遞給該方法一個新的、空數據庫,必須完全初始化它。在SimpleVideoDbHelper中,這是個簡單的任務,是通過調用createVideosTable完成的。
onUpdate
SimpleVideoContentProvider的onUpdate方法非常簡單:它刪除數據庫。當provider在後期要使用數據庫時,Android會調用onCreate方法,因為數據庫不存在。雖然在這個非常簡單的例子中,這種方式可能是可以接受的,provider只是作為網絡數據的緩存,它肯定不適合作為聯繫方式數據庫。如果你的客戶在升級軟件版本時,需要每次重新設置信息,他們肯定很不樂意。因此,在現實中,該onUpdate方法沒有什麼用。通常而言,onUpdate方法需要識別應用使用的所有之前版本的數據庫,並通過數據安全的方式把這些數據庫轉化成最新的形式。更大的應用可能會有一些升級腳本,每個版本包含一個腳本。應用可以依次執行每個升級腳本,直到數據庫完全更新。
createTable
我們創建這個函數,對創建表的SQL代碼進行封裝。