1. SQLite輕裝上陣
SQLite是一個輕量級的數據庫,它和筆者之前接觸的SQLServer或Oracle DB比起來,猶如螞蟻和大象的區別。它“輕”到什么程度呢?筆者總結了SQLite具有的兩個特點:
- 從代碼上看,SQLite所有的功能都實現在Sqlite3.c中,而頭文件Sqlite3.h定義了它所支持的API。其中,Sqlite3.c文件包含12萬行左右的代碼,相當于一個中等偏小規模的程序。
- 從使用者角度的來說,SQLite編譯完成后將生成一個libsqlite.so,大小僅為300多KB。
SQLite確實夠輕,但這個“輕”還不是本節標題“輕裝上陣”一詞中的“輕”。為什么?此處先向讀者們介紹一個直接使用SQLite API開發的Android native程序示例,該示例最終編譯成的二進制可執行程序名為sqlitetest。
>[info] **注意**:本書后文所說的SQLite API特指libsqlite.so提供的Native層的API。
使用SQLite API開發的Android native程序示例的代碼如下:
**SqliteTest.cpp::libsqlite示例**
~~~
#include <unistd.h>
#include <sqlite3.h> //包含sqlite API頭文件,這里的3是SQLite的版本號
#include <stdlib.h>
#define LOG_TAG "SQLITE_TEST" //定義該進程logcat輸出的標簽
#include <utils/Log.h>
#ifndef NULL
#defineNULL (0)
#endif
//聲明數據庫文件的路徑
#define DB_PATH"/mnt/sdcard/sqlite3test.db"
/*
聲明一個全局的SQLite句柄,開發者無需了解該數據結構的具體內容,只要知道它代表了使用者
和數據庫的一種連接關系即可。以后凡是針對特定數據庫的操作,都需要傳入對應的SQLite句柄
*/
static sqlite3* g_pDBHandle = NULL;
/*
定義一個宏,用于檢測SQLite API調用的返回值,如果value不等于expectValue,則打印警告,
并退出程序。注意,進程退出后,系統會自動回收分配的內存資源。對如此簡單的例子來說,讀者大可
不必苛責。其中,sqlite3_errmsg函數用于打印錯誤信息
*/
#define CHECK_DB_ERR(value,expectValue) \
do \
{ \
if(value!= expectValue)\
{\
LOGE("Sqlite db fail:%s",g_pDBHandle==NULL?"db not \
connected":sqlite3_errmsg(g_pDBHandle));\
exit(0);\
}\
}while(0)
int main(int argc, char* argv[])
{
LOGD("Delete old DB file");
unlink(DB_PATH);//先刪除舊的數據庫文件
LOGD("Create new DB file");
/*
調用sqlite3_open創建一個數據庫,并將和該數據庫的連接環境保存在全局的SQLite句柄
g_pDBHandle中,以后操作g_pDBHandle就是操作DB_PATH對應的數據庫
*/
int ret =sqlite3_open(DB_PATH,&g_pDBHandle);
CHECK_DB_ERR(ret,SQLITE_OK);
LOGD("Create Table personal_info:");
/*
定義宏TABLE_PERSONAL_INFO,用于描述為本例數據庫建立一個表所用的SQL語句,
不熟悉SQL語句的讀者可先學習相關知識。從該宏的定義可知,將建立一個名為
personal_info的表,該表有4列,第一列是主鍵,類型是整型(SQLite中為INTEGER),
每加入一行數據該值會自動遞增;第二列名為"name",數據類型是字符串(SQLite中為TEXT);
第三列名為“age”,數據類型是整型;第四列名為“sex”,數據類型是字符串
*/
#defineTABLE_PERSONAL_INFO \
"CREATETABLE personal_info" \
"(ID INTEGER primary keyautoincrement," \
"nameTEXT," \
"age INTEGER,"\
"sex TEXT"\
")"
//打印TABLE_PERSONAL_INFO所對應的SQL語句
LOGD("\t%s\n",TABLE_PERSONAL_INFO);
//調用sqlite3_exec執行前面那條SQL語句
ret =sqlite3_exec(g_pDBHandle,TABLE_PERSONAL_INFO,NULL,NULL,NULL);
CHECK_DB_ERR(ret,SQLITE_OK);
/*
定義插入一行數據所使用的SQL語句,注意最后一行中的問號,它表示需要在插入數據
前和具體的值綁定。插入數據庫對應的SQL語句是標準的INSERT命令
*/
LOGD("Insert one personal info into personal_info table");
#defineINSERT_PERSONAL_INFO \
"INSERT INTO personal_info"\
"(name,age,sex)"\
"VALUES"\
"(?,?,?)" //注意這一行語句中的問號
LOGD("\t%s\n",INSERT_PERSONAL_INFO);
//sqlite3_stmt是SQLite中很重要的一個結構體,它代表了一條SQL語句
sqlite3_stmt* pstmt = NULL;
/*
調用sqlite3_prepare初始化pstmt,并將其和INSERT_PERSONAL_INFO綁定。
也就是說,如果執行pstmt,就會執行INSERT_PERSONAL_INFO語句
*/
ret =sqlite3_prepare(g_pDBHandle,INSERT_PERSONAL_INFO,-1,&pstmt,NULL);
CHECK_DB_ERR(ret,SQLITE_OK);
/*
調用sqlite3_bind_xxx為該pstmt中對應的問號綁定具體的值,該函數的第二個參數用于
指定第幾個問號
*/
ret =sqlite3_bind_text(pstmt,1,"dengfanping",-1,SQLITE_STATIC);
ret =sqlite3_bind_int(pstmt,2,30);
ret =sqlite3_bind_text(pstmt,3,"male",-1,SQLITE_STATIC);
//調用sqlite3_step執行對應的SQL語句,該函數如果執行成功,我們的personal_info
//表中將添加一條新記錄,對應值為(1,dengfanping,30,male)
ret =sqlite3_step(pstmt);
CHECK_DB_ERR(ret,SQLITE_DONE);
//調用sqlite3_finalize銷毀該SQL語句
ret =sqlite3_finalize(pstmt);
//下面將從表中查詢name為"dengfanping"的person的age值
LOGD("select dengfanping's age from personal_info table");
pstmt =NULL;
/*
重新初始化該pstmt,并將其和SQL語句“SELECT age FROM personal_info WHERE name
= ?”綁定
*/
ret =sqlite3_prepare(g_pDBHandle,"SELECT age FROM personal_info WHERE name
=? ",-1,&pstmt,NULL);
CHECK_DB_ERR(ret,SQLITE_OK);
/*
綁定pstmt中第一個問號為字符串“dengfanping”,最終該SQL語句為
SELECTage FROM personal_info WHERE name = 'dengfanping'
*/
ret =sqlite3_bind_text(pstmt,1,"dengfanping",-1,SQLITE_STATIC);
CHECK_DB_ERR(ret,SQLITE_OK);
//執行這條查詢語句
while(true)//在一個循環中遍歷結果集
{
ret =sqlite3_step(pstmt);
if(ret ==SQLITE_ROW)
{
//從結果集中取出第一列(由于執行SELECT時只選擇了age,故最終結果只有一列),
//調用sqlite3_column_int返回結果集的第一列(從0開始)第一行的值
intmyage = sqlite3_column_int(pstmt, 0);
LOGD("Gotdengfanping's age: %d\n",myage);
}
else //如果ret為其他值,則退出循環
break;
}
else LOGD("Find nothing\n"); //SELECT執行失敗
ret =sqlite3_finalize(pstmt);//銷毀pstmt
if(g_pDBHandle)
{
LOGD("CloseDB");
//調用sqlite3_close關閉數據庫連接并釋放g_pDBHandle
sqlite3_close(g_pDBHandle);
g_pDBHandle = NULL;
}
return 0;
}
~~~
通過上述示例代碼可發現,SQLite API的使用主要集中在以下幾點上:
- 創建代表指定數據庫的sqlite3實例。
- 創建代表一條SQL語句的sqlite3_stmt實例,在使用過程中首先要調用sqlite3_prepare將其和一條代表SQL語句的字符串綁定。如該字符串含有通配符“?”,后續就需要通過sqlite3_bind_xxx函數為通配符綁定特定的值以生成一條完整的SQL語句。最終調用sqlite3_step執行這條語句。
- 如果是查詢(即SELECT命令)命令,則需調用sqlite3_step函數遍歷結果集,并通過sqlite3_column_xx等函數取出結果集中某一行指定列的值。
- 最后需調用sqlite3_finalize和sqlite3_close來釋放sqlite3_stmt實例及sqlite3實例所占據的資源。
SQLite API的使用非常簡單明了。不過很可惜,這份簡單明了所帶來的快捷,只供那些Native層程序開發者獨享。對于Java程序員,他們只能使用Android在SQLite API之上所封裝的SQLiteDatabase家族提供的類和相關API。筆者心中對這個封裝的評價只有一個詞 “嘆為觀止”,綜合考慮到架構及系統的穩定性和可擴展性等各種情況,Android在 SQLite API之上進行了面向對象的封裝和解耦等設計,最終呈現在大家面前的是一個龐大而復雜的SQLiteDatabase家族,其成員有61個之多(參閱frameworks/base/core/java/android/database目錄中的文件)。
現在讀者應該理解本節標題“SQLite輕裝上陣”中“輕”的真正含義了。在后續的分析過程中,我們主要和SQLiteDatabase家族打交道。隨著分析的深入,讀者能逐漸見認識SQLiteDatabase的“厚重"。
2. SQLiteDatabase家族介紹
圖7-4展示了SQLiteDatabase家族中的幾位重要成員。
:-: 
圖7-4 SQLitDatabase家族部分成員
圖7-4中相關類的說明如下:
- SQLiteOpenHelper是一個幫助(Utility)類,用于方便開發者創建和管理數據庫。
- SQLiteQueryBuilder是一個幫助類,用于幫助開發者創建SQL語句。
- SQLiteDatabase代表SQLite數據庫,它內部封裝了一個Native層的sqlite3實例。
- Android提供了3個類SQLiteProgram、SQLiteQuery和SQLiteStatement用于描述和SQL語句相關的信息。從圖7-4可知,SQLiteProgram是基類,它提供了一些API用于參數綁定。SQLiteQuery主要用于query查詢操作,而SQLiteStatement用于query之外的一些操作(根據SDK的說明,如果SQLiteStatement用于query查詢,其返回的結果集只能是1行*1列)。注意,在這3個類中,基類SQLiteProgram將保存一個指向Native層的sqlite3_stmt實例的變量,但是這個成員變量的賦值卻和另外一個對開發者隱藏的類SQLiteComplieSql有關。從這個角度看,可以認為Native層sqlite3_stmt實例的封裝是由SQLiteComplieSql完成的。這方面的知識在后文進行分析時即能見到。
- SQLiteClosable用于控制SQLiteDatabase家族中一些類的實例的生命周期,例如SQLiteDatabase實例和SQLiteQuery實例。每次使用這些實例對象前都需要調用acquireReference以增加引用計數,使用完畢后都需要調用releaseReferenece以減少引用計數。
* * * * *
**提示**:讀者見識了SQLiteDatabase家族中這幾位成員后有何感想?是否覺得要真正搞清楚它們還需要花費一番工夫呢?
* * * * *
下面來看MediaProvider是如何使用SQLiteDatabase的,重點關注SQLite數據庫是如何創建的。
- 前言
- 第1章 搭建Android源碼工作環境
- 1.1 Android系統架構
- 1.2 搭建開發環境
- 1.2.1 下載源碼
- 1.2.2 編譯源碼
- 1.2.3 利用Eclipse調試system_process
- 1.3 本章小結
- 第2章 深入理解Java Binder和MessageQueue
- 2.1 概述
- 2.2 Java層中的Binder架構分析
- 2.2.1 Binder架構總覽
- 2.2.2 初始化Java層Binder框架
- 2.2.3 addService實例分析
- 2.2.4 Java層Binder架構總結
- 2.3 心系兩界的MessageQueue
- 2.3.1 MessageQueue的創建
- 2.3.2 提取消息
- 2.3.3 nativePollOnce函數分析
- 2.3.4 MessageQueue總結
- 2.4 本章小結
- 第3章 深入理解SystemServer
- 3.1 概述
- 3.2 SystemServer分析
- 3.2.1 main函數分析
- 3.2.2 Service群英會
- 3.3 EntropyService分析
- 3.4 DropBoxManagerService分析
- 3.4.1 DBMS構造函數分析
- 3.4.2 dropbox日志文件的添加
- 3.4.3 DBMS和settings數據庫
- 3.5 DiskStatsService和DeviceStorageMonitorService分析
- 3.5.1 DiskStatsService分析
- 3.5.2 DeviceStorageManagerService分析
- 3.6 SamplingProfilerService分析
- 3.6.1 SamplingProfilerService構造函數分析
- 3.6.2 SamplingProfilerIntegration分析
- 3.7 ClipboardService分析
- 3.7.1 復制數據到剪貼板
- 3.7.2 從剪切板粘貼數據
- 3.7.3 CBS中的權限管理
- 3.8 本章小結
- 第4章 深入理解PackageManagerService
- 4.1 概述
- 4.2 初識PackageManagerService
- 4.3 PKMS的main函數分析
- 4.3.1 構造函數分析之前期準備工作
- 4.3.2 構造函數分析之掃描Package
- 4.3.3 構造函數分析之掃尾工作
- 4.3.4 PKMS構造函數總結
- 4.4 APK Installation分析
- 4.4.1 adb install分析
- 4.4.2 pm分析
- 4.4.3 installPackageWithVerification函數分析
- 4.4.4 APK 安裝流程總結
- 4.4.5 Verification介紹
- 4.5 queryIntentActivities分析
- 4.5.1 Intent及IntentFilter介紹
- 4.5.2 Activity信息的管理
- 4.5.3 Intent 匹配查詢分析
- 4.5.4 queryIntentActivities總結
- 4.6 installd及UserManager介紹
- 4.6.1 installd介紹
- 4.6.2 UserManager介紹
- 4.7 本章學習指導
- 4.8 本章小結
- 第5章 深入理解PowerManagerService
- 5.1 概述
- 5.2 初識PowerManagerService
- 5.2.1 PMS構造函數分析
- 5.2.2 init分析
- 5.2.3 systemReady分析
- 5.2.4 BootComplete處理
- 5.2.5 初識PowerManagerService總結
- 5.3 PMS WakeLock分析
- 5.3.1 WakeLock客戶端分析
- 5.3.2 PMS acquireWakeLock分析
- 5.3.3 Power類及LightService類介紹
- 5.3.4 WakeLock總結
- 5.4 userActivity及Power按鍵處理分析
- 5.4.1 userActivity分析
- 5.4.2 Power按鍵處理分析
- 5.5 BatteryService及BatteryStatsService分析
- 5.5.1 BatteryService分析
- 5.5.2 BatteryStatsService分析
- 5.5.3 BatteryService及BatteryStatsService總結
- 5.6 本章學習指導
- 5.7 本章小結
- 第6章 深入理解ActivityManagerService
- 6.1 概述
- 6.2 初識ActivityManagerService
- 6.2.1 ActivityManagerService的main函數分析
- 6.2.2 AMS的 setSystemProcess分析
- 6.2.3 AMS的 installSystemProviders函數分析
- 6.2.4 AMS的 systemReady分析
- 6.2.5 初識ActivityManagerService總結
- 6.3 startActivity分析
- 6.3.1 從am說起
- 6.3.2 AMS的startActivityAndWait函數分析
- 6.3.3 startActivityLocked分析
- 6.4 Broadcast和BroadcastReceiver分析
- 6.4.1 registerReceiver流程分析
- 6.4.2 sendBroadcast流程分析
- 6.4.3 BROADCAST_INTENT_MSG消息處理函數
- 6.4.4 應用進程處理廣播分析
- 6.4.5 廣播處理總結
- 6.5 startService之按圖索驥
- 6.5.1 Service知識介紹
- 6.5.2 startService流程圖
- 6.6 AMS中的進程管理
- 6.6.1 Linux進程管理介紹
- 6.6.2 關于Android中的進程管理的介紹
- 6.6.3 AMS進程管理函數分析
- 6.6.4 AMS進程管理總結
- 6.7 App的 Crash處理
- 6.7.1 應用進程的Crash處理
- 6.7.2 AMS的handleApplicationCrash分析
- 6.7.3 AppDeathRecipient binderDied分析
- 6.7.4 App的Crash處理總結
- 6.8 本章學習指導
- 6.9 本章小結
- 第7章 深入理解ContentProvider
- 7.1 概述
- 7.2 MediaProvider的啟動及創建
- 7.2.1 Context的getContentResolver函數分析
- 7.2.2 MediaStore.Image.Media的query函數分析
- 7.2.3 MediaProvider的啟動及創建總結
- 7.3 SQLite創建數據庫分析
- 7.3.1 SQLite及SQLiteDatabase家族
- 7.3.2 MediaProvider創建數據庫分析
- 7.3.3 SQLiteDatabase創建數據庫的分析總結
- 7.4 Cursor 的query函數的實現分析
- 7.4.1 提取query關鍵點
- 7.4.2 MediaProvider 的query分析
- 7.4.3 query關鍵點分析
- 7.4.4 Cursor query實現分析總結
- 7.5 Cursor close函數實現分析
- 7.5.1 客戶端close的分析
- 7.5.2 服務端close的分析
- 7.5.3 finalize函數分析
- 7.5.4 Cursor close函數總結
- 7.6 ContentResolver openAssetFileDescriptor函數分析
- 7.6.1 openAssetFileDescriptor之客戶端調用分析
- 7.6.2 ContentProvider的 openTypedAssetFile函數分析
- 7.6.3 跨進程傳遞文件描述符的探討
- 7.6.4 openAssetFileDescriptor函數分析總結
- 7.7 本章學習指導
- 7.8 本章小結
- 第8章 深入理解ContentService和AccountManagerService
- 8.1 概述
- 8.2 數據更新通知機制分析
- 8.2.1 初識ContentService
- 8.2.2 ContentResovler 的registerContentObserver分析
- 8.2.3 ContentResolver的 notifyChange分析
- 8.2.4 數據更新通知機制總結和深入探討
- 8.3 AccountManagerService分析
- 8.3.1 初識AccountManagerService
- 8.3.2 AccountManager addAccount分析
- 8.3.3 AccountManagerService的分析總結
- 8.4 數據同步管理SyncManager分析
- 8.4.1 初識SyncManager
- 8.4.2 ContentResolver 的requestSync分析
- 8.4.3 數據同步管理SyncManager分析總結
- 8.5 本章學習指導
- 8.6 本章小結