在MediaProvider中觸發數據庫的是attach函數,其代碼如下:
**MediaProvider::attach**
~~~
private Uri attachVolume(String volume) {
Contextcontext = getContext();
DatabaseHelper db;
if(INTERNAL_VOLUME.equals(volume)) {
......//針對內部存儲空間的數據庫
} elseif (EXTERNAL_VOLUME.equals(volume)) {
......
String dbName = "external-" +Integer.toHexString(volumeID) + ".db";
//①構造一個DatabaseHelper對象
db =new DatabaseHelper(context, dbName, false,
false, mObjectRemovedCallback);
......//省略不相關的內容
}......
if(!db.mInternal) {
//②調用DatabaseHelper的getWritableDatabase函數,該函數返回值的類型為
//SQLiteDatabase,即代表SQLite數據庫的對象
createDefaultFolders(db.getWritableDatabase());
.......
}
......
}
~~~
以上代碼中列出了兩個關鍵點,分別是:
- 構造一個DatabaseHelper對象。
- 調用DatabaseHelper對象的getWritableDatabase函數得到一個代表SQLite數據庫的SQLiteDatabase對象。
1. DatabaseHelper分析
DatabaseHelper是MediaProvider的內部類,它從SQLiteOpenHelper派生,其構造函數的代碼如下:
(1) DatabaseHelper構造函數分析
**MediaProvider.java::DatabaseHelper**
~~~
public DatabaseHelper(Context context, Stringname, boolean internal,
boolean earlyUpgrade,
SQLiteDatabase.CustomFunction objectRemovedCallback) {
//重點關注其基類的構造函數
super(context, name, null,DATABASE_VERSION);
mContext = context;
mName= name;
mInternal = internal;
mEarlyUpgrade = earlyUpgrade;
mObjectRemovedCallback = objectRemovedCallback;
}
~~~
SQLiteOpenHelper作為DatabaseHelper的基類,其構造函數的代碼如下:
**SQLiteOpenHelper.java::SQLiteOpenHelper**
~~~
public SQLiteOpenHelper(Context context, Stringname, CursorFactory factory,
int version) {
//調用另外一個構造函數,注意它新建了一個默認的錯誤處理對象
this(context, name, factory, version, newDefaultDatabaseErrorHandler());
}
public SQLiteOpenHelper(Context context, Stringname, CursorFactory factory,
int version, DatabaseErrorHandlererrorHandler) {
......
mContext= context;
mName =name;
//看到”factory“一詞,讀者要能想到設計模式中的工廠模式,在本例中該變量為null
mFactory= factory;
mNewVersion = version;
mErrorHandler= errorHandler;
}
~~~
上面這些函數都比較簡單,其中卻蘊含一個較為深刻的設計理念,具體如下:
從SQLiteOpenHelper的構造函數中可知,MediaProvider對應的數據庫對象(即SQLiteDatabase實例)并不在該函數中創建。那么,代表數據庫的SQLiteDatabase實例是何時創建呢?
此處使用了所謂的延遲創建(lazy creation)的方法,即SQLiteDatabase實例真正創建的時機是在第一次使用它的時候,也就是本例中第二個關鍵點函數getWritableDatabase。
在分析getWritableDatabase函數之前,先介紹一些和延遲創建相關的知識。
延遲創建或延遲初始化(lazy intializtion)所謂的“重型”資源(如占內存較大或創建時間比較長的資源),是系統開發和設計中常用的一種策略[^①]。在使用這種策略時,開發人員不僅在資源創建時“斤斤計較”,在資源釋放的問題上也是“慎之又。資源釋放的控制一般會采用引用計數技術。
結合前面對SQLiteDatabase的介紹會發現,SQLiteDatabase這個框架,在設計時不是簡單地將SQLite API映射到Java層,而是有大量更為細致的考慮。例如,在這個框架中,資源創建采用了lazy creation方法,資源釋放又利用SQLiteClosable來控制生命周期。
* * * * *
**建議**:此框架雖更完善、更具擴展性,但是使用它比直接使用SQLite API要復雜得多,因此在開發過程中,應當根據實際情況綜合考慮是否使用該框架。例如,筆者在開發公司的DLNA解決方案時,就直接使用了SQLiteAPI,而沒使用這個框架。
* * * * *
(2) getWritableDatabase函數分析
現在來看getWritableDatabase的代碼。
**SQLiteDatabase.java::getWritableDatabase**
~~~
public synchronized SQLiteDatabasegetWritableDatabase() {
if(mDatabase != null) {
//第一次調用該函數時mDatabase還未創建。以后的調用將直接返回已經創建好的mDatabase
}
booleansuccess = false;
SQLiteDatabase db = null;
if (mDatabase != null) mDatabase.lock();
try {
mIsInitializing = true;
if(mName == null) {
db = SQLiteDatabase.create(null);
}else {
//①調用Context的openOrCreateDatabase創建數據庫
db = mContext.openOrCreateDatabase(mName, 0,
mFactory, mErrorHandler);
}
intversion = db.getVersion();
if(version != mNewVersion) {
db.beginTransaction();
try {
if (version == 0) {
/*
如果初次創建該數據庫(即對應的數據庫文件不存在),則調用子類實現的
onCreate函數。子類實現的onCreate函數將完成數據庫建表等操作。讀者不妨
查看MediaProviderDatabaseHelper實現的onCreate函數
*/
onCreate(db);
} else {
//如果從數據庫文件中讀出來的版本號與MediaProvider設置的版本號不一致,
//則調用子類實現的onDowngrade或onUpgrade做相應處理
if (version > mNewVersion)
onDowngrade(db, version,mNewVersion);
else
onUpgrade(db, version,mNewVersion);
}
db.setVersion(mNewVersion);
db.setTransactionSuccessful();
}finally {
db.endTransaction();
}
}// if (version != mNewVersion)判斷結束
onOpen(db);//調用子類實現的onOpen函數
success =true;
return db;
}......
~~~
由以上代碼可知,代表數據庫的SQLiteDatabase對象是由context openOrCreateDatabase創建的。下面單起一節具體分析此函數。
2. ContextImpl openOrCreateDatabase分析
(1) openOrCreateDatabase函數分析
相信讀者已能準確定位openOrCreateDatabase函數的真正實現了,它就在ContextImpl.java中,其代碼如下:
**ContextImpl.java::openOrCreateDatabase**
~~~
public SQLiteDatabase openOrCreateDatabase(Stringname, int mode,
CursorFactory factory,DatabaseErrorHandler errorHandler) {
File f =validateFilePath(name, true);
//調用SQLiteDatabase的靜態函數openOrCreateDatabase創建數據庫
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f.getPath(),
factory, errorHandler);
setFilePermissionsFromMode(f.getPath(), mode, 0);
return db;
}
~~~
**SQLiteDatabase.java::openDatabase**
~~~
public static SQLiteDatabase openDatabase(Stringpath, CursorFactory
factory, int flags,DatabaseErrorHandlererrorHandler) {
//又調用openDatabase創建SQLiteDatabase實例,真的是層層轉包啊
SQLiteDatabase sqliteDatabase = openDatabase(path, factory,
flags, errorHandler,(short) 0);
if(sBlockSize == 0) sBlockSize = newStatFs("/data").getBlockSize();
//為該SQLiteDatabase實例設置一些參數。這些內容和SQLite本身的特性有關,本書不
//擬深入討論這方面的內容,感興趣的讀者不妨參考SQLite官網提供的資料
sqliteDatabase.setPageSize(sBlockSize);
sqliteDatabase.setJournalMode(path, "TRUNCATE");
synchronized(mActiveDatabases) {
mActiveDatabases.add(
newWeakReference<SQLiteDatabase>(sqliteDatabase));
}
returnsqliteDatabase;
}
~~~
openDatabase將真正創建一個SQLiteDatabase實例,其相關代碼是:
**SqliteDatabase.java::openDatabase**
~~~
private static SQLiteDatabase openDatabase(Stringpath, CursorFactory factory,
int flags,DatabaseErrorHandler errorHandler,
shortconnectionNum) {
//構造一個SQLiteDatabase實例
SQLiteDatabase db = new SQLiteDatabase(path, factory, flags,errorHandler,
connectionNum);
try {
db.dbopen(path, flags);//打開數據庫,dbopen是一個native函數
db.setLocale(Locale.getDefault());//設置Locale
......
returndb;
}......
}
~~~
其實openDatabase主要就干了兩件事情,即創建一個SQLiteDatabase實例,然后調用該實例的dbopen函數。
(2) SQLiteDatabase的構造函數及dbopen函數分析
先看SQLitedDatabase的構造函數,代碼如下:
**SQLitedDatabase.java::SQLiteDatabas**e
~~~
private SQLiteDatabase(String path, CursorFactoryfactory, int flags,
DatabaseErrorHandler errorHandler, short connectionNum) {
setMaxSqlCacheSize(DEFAULT_SQL_CACHE_SIZE);
mFlags =flags;
mPath = path;
mFactory= factory;
mPrograms= new WeakHashMap<SQLiteClosable,Object>();
// config_cursorWindowSize值為2048,所以下面得到的limit值應該為8MB
int limit= Resources.getSystem().getInteger(
com.android.internal.R.integer.config_cursorWindowSize)
* 1024 * 4;
native_setSqliteSoftHeapLimit(limit);
}
~~~
前面說過,Java層的SQLiteDatabase對象會和一個Native層sqlite3實例綁定,從以上代碼中可發現,綁定的工作并未在構造函數中開展。實際上,該工作是由dbopen函數完成的,其相關代碼如下:
**android_database_SQLiteDatabase.cpp::dbopen**
~~~
static void dbopen(JNIEnv* env, jobject object,jstring pathString, jint flags)
{
int err;
sqlite3* handle = NULL;
sqlite3_stmt * statement = NULL;
charconst * path8 = env->GetStringUTFChars(pathString, NULL);
intsqliteFlags;
registerLoggingFunc(path8);
if(flags & CREATE_IF_NECESSARY) {
sqliteFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
} ......
//調用sqlite3_open_v2函數創建數據庫,sqlite3_open_v2和示例中的sqlite3_open類似
//handle用于存儲新創建的sqlite3*類型的實例
err =sqlite3_open_v2(path8, &handle, sqliteFlags, NULL);
......
sqlite3_soft_heap_limit(sSqliteSoftHeapLimit);
err =sqlite3_busy_timeout(handle, 1000 /* ms */);
......
//Android在原生SQLite之上還做了一些特殊的定制,相關內容留待本節最后分析
err =register_android_functions(handle, UTF16_STORAGE);
//將handle保存到Java層的SQLiteDatabase對象中,這樣Java層SQLiteDatabase實例
//就和一個Native層的sqlite3實例綁定到一起了
env->SetIntField(object, offset_db_handle, (int) handle);
handle =NULL; // The caller owns the handle now.
done:
if(path8 != NULL) env->ReleaseStringUTFChars(pathString, path8);
if(statement != NULL) sqlite3_finalize(statement);
if(handle != NULL) sqlite3_close(handle);
}
~~~
從上述代碼可知,使用dbopen函數其實就是為了得到Native層的一個sqlite3實例。另外,Android對SQLite還設置了一些平臺相關的函數,這部分內容將在后文進行分析。
3. SQLiteCompiledSql介紹
前文曾提到,Native層sqlite3_stmt實例的封裝是由未對開發者公開的類SQLiteCompileSql完成的。由于它的隱秘性,沒有在圖7-4中把它列出來。現在我們就來揭開它神秘的面紗,其代碼如下:
**SQLiteCompliteSql.java::SQLiteCompiledSql**
~~~
SQLiteCompiledSql(SQLiteDatabase db, String sql) {
db.verifyDbIsOpen();
db.verifyLockOwner();
mDatabase = db;
mSqlStmt= sql;
......
nHandle= db.mNativeHandle;
native_compile(sql);//調用native_compile函數,代碼如下
}
~~~
**android_database_SQLiteCompliteSql.cpp::native_compile**
~~~
static void native_compile(JNIEnv* env, jobjectobject, jstring sqlString)
{
compile(env, object, GET_HANDLE(env, object), sqlString);
}
//來看compile的實現
sqlite3_stmt * compile(JNIEnv* env, jobjectobject,
sqlite3 * handle,jstring sqlString)
{
int err;
jcharconst * sql;
jsizesqlLen;
sqlite3_stmt * statement = GET_STATEMENT(env, object);
if(statement != NULL) ....//釋放之前的sqlite3_stmt實例
sql = env->GetStringChars(sqlString,NULL);
sqlLen =env->GetStringLength(sqlString);
//調用sqlite3_prepare16_v2得到一個sqlite3_stmt實例
err =sqlite3_prepare16_v2(handle, sql, sqlLen * 2, &statement, NULL);
env->ReleaseStringChars(sqlString, sql);
if (err== SQLITE_OK) {
//保存到Java層的SQLiteCompliteSql對象中
env->SetIntField(object, gStatementField, (int)statement);
return statement;
} ......
}
~~~
當compile函數執行完后,一個綁定了SQL語句的sqlite3_stmt實例就和Java層的SQLiteCompileSql對象綁定到一起了。
4. Android SQLite自定義函數介紹
本節將介紹Android在SQLite上自定義的一些函數。一切還得從SQL的觸發器說起。
(1) 觸發器介紹
觸發器(Trigger)是數據庫開發技術中一個常見的術語。其本質非常簡單,就是在指定表上發生特定事情時,數據庫需要執行的某些操作。還是有點模糊吧?再來看MediaProvider設置的一個觸發器:
~~~
db.execSQL("CREATE TRIGGER IF NOT EXISTSimages_cleanup DELETE ON images " +
"BEGIN " +
"DELETE FROM thumbnails WHERE image_id = old._id;" +
"SELECT _DELETE_FILE(old._data);" +
"END");
~~~
上面這條SQL語句是什么意思呢?
- CREATE TRIGGER IF NOT EXITSimages_cleanup:如果沒有定義名為images_cleanup的觸發器,就創建一個名為images_cleanup的觸發器。
- DELETE ON images:設置該觸發器的觸發條件。顯然,當我們對images表執行delete操作時,該觸發器將被觸發。
BEGIN和END之間則定義了該觸發器要執行的動作。從前面的代碼可知,它將執行兩項操作:
- 刪除thumbnails(縮略圖)表中對應的信息。為什么要刪除縮略圖呢?因為原圖的信息已經不存在了,留著它沒用。
- 執行_DELETE_FILE函數,其參數是old.data。從名字上來看,這個函數的功能應為刪除文件。為什么要刪除此文件?原因也很簡單,數據庫都沒有該項信息了,還留著圖片干什么!另外,如不刪除文件,下一次媒體掃描時就又會把它們找到。
* * * * *
**提示**:_DELETE_FILE這個操作曾給筆者及同仁帶來極大困擾,因為最開始并不知道有這個觸發器。結果好不容易下載的測試文件全部被刪除了。另外,由于MediaProvider本身的設計缺陷,頻繁掛/卸載SD卡時也會錯誤刪除數據庫信息(這個缺陷只能盡量避免,無法徹底根除),結果實體文件也被刪除掉了。
* * * * *
有人可能會感到奇怪,這個_DELETE_FILE函數是誰設置的呢?答案就在前面提到的register_android_functions中。
(2) register_android_functions介紹
register_android_functions在dbopen中被調用,其代碼如下:
**sqlite3_android.cpp::register_android_functions**
~~~
//dbopen調用它時,第二個參數設置為0
extern "C" intregister_android_functions(sqlite3 * handle, int utf16Storage)
{
int err;
UErrorCode status = U_ZERO_ERROR;
UCollator * collator = ucol_open(NULL, &status);
......
if(utf16Storage) {
err =sqlite3_exec(handle, "PRAGMA encoding = 'UTF-16'", 0, 0, 0);
......
} else {
//sqlite3_create_collation_xx定義一個用于排序的文本比較函數,讀者可自行閱讀
//SQLite官方文檔以獲得更詳細的說明
err = sqlite3_create_collation_v2(handle,"UNICODE",
SQLITE_UTF8, collator, collate8,
(void(*)(void*))localized_collator_dtor);
}
/*
調用sqlite3_create_function創建一個名為"PHONE_NUMBERS_EQUAL"的函數,
第三個參數2表示該函數有兩個參數,SQLITE_UTF8表示字符串編碼為UTF8,
phone_numbers_equal為該函數對應的函數指針,也就是真正會執行的函數。注意
"PHONE_NUMBERS_EQUAL"是SQL語句中使用的函數名,phone_numbers_equal是Native
層對應的函數
*/
err =sqlite3_create_function(
handle, "PHONE_NUMBERS_EQUAL", 2,
SQLITE_UTF8, NULL, phone_numbers_equal, NULL, NULL);
......
//注冊_DELETE_FILE對應的函數為delete_file
err =sqlite3_create_function(handle, "_DELETE_FILE", 1, SQLITE_UTF8,
NULL, delete_file, NULL, NULL);
if (err!= SQLITE_OK) {
return err;
}
#if ENABLE_ANDROID_LOG
err =sqlite3_create_function(handle, "_LOG", 1, SQLITE_UTF8,
NULL, android_log,NULL, NULL);
......
#endif
......//和PHONE相關的一些函數
returnSQLITE_OK;
}
~~~
register_android_functions注冊了Android平臺上定制的一些函數。來看和_DELETE_FILE有關的delete_file函數,其代碼為:
**Sqlite3_android.cpp::delete_file**
~~~
static void delete_file(sqlite3_context * context,int argc,
sqlite3_value** argv)
{
if (argc!= 1) {
sqlite3_result_int(context, 0);
return;
}
//從argv中取出第一個參數,這個參數是觸發器調用_DELETE_FILE時傳遞的
charconst * path = (char const *)sqlite3_value_text(argv[0]);
......
/*
Android4.0之后,系統支持多個存儲空間(很多平板都有一塊很大的內部存儲空間)。
為了保持兼容性,環境變量EXTERNAL_STORAGE還是指向sd卡的掛載目錄,而其他存儲設備的
掛載目錄由SECCONDARY_STORAGE表示,各個掛載目錄由冒號分隔。
下面這段代碼用于判斷_DELETE_FILE函數所傳遞的文件路徑是不是正確的
*/
boolgood_path = false;
charconst * external_storage = getenv("EXTERNAL_STORAGE");
if(external_storage && strncmp(external_storage,
path,strlen(external_storage)) == 0) {
good_path = true;
} else {
charconst * secondary_paths = getenv("SECONDARY_STORAGE");
while (secondary_paths && secondary_paths[0]) {
const char* colon = strchr(secondary_paths, ':');
int length = (colon ? colon - secondary_paths :
strlen(secondary_paths));
if (strncmp(secondary_paths, path, length) == 0) {
good_path = true;
}
secondary_paths += length;
while (*secondary_paths == ':')secondary_paths++;
}
}
if(!good_path) {
sqlite3_result_null(context);
return;
}
//調用unlink刪除文件
int err= unlink(path);
if (err!= -1) {
sqlite3_result_int(context, 1);//設置返回值
} else {
sqlite3_result_int(context, 0);
}
}
~~~
[^①]:其實這是一種廣義的設計模式,讀者可參考《Pattern-Oriented Software Architecture Volume 3: Patterns forResource Management》一書以加深理解。
- 前言
- 第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 本章小結