本節將分析MediaProvider query函數。此次分析的焦點不是MediaProvider本身的業務邏輯,而是要搞清query函數返回的Cursor到底是什么,其代碼如下,:
**MediaProvider.java::query**
~~~
public Cursor query(Uri uri, String[]projectionIn, String selection,
String[] selectionArgs, String sort) {
int table= URI_MATCHER.match(uri);
......
//根據uri取出對應的DatabaseHelper對象,MediaProvider針對內部存儲中的媒體文件和
//外部存儲(即SD卡)中的媒體文件分別創建了兩個數據庫
DatabaseHelper database = getDatabaseForUri(uri);
//我們在7.3.2節分析過getReadableDatabase函數的兄弟getWritableDatabase函數
SQLiteDatabase db = database.getReadableDatabase();
//創建一個SQLiteQueryBuilder對象,用于方便開發人員編寫SQL語句
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
......//設置qb的參數,例如調用setTables函數為本次查詢設定目標Table
//①調用SQLiteQueryBuilder的query函數
Cursor c =qb.query(db, projectionIn, selection,
combine(prependArgs, selectionArgs), groupBy, null, sort, limit);
if (c !=null) {
//2調用該Cursor對象的setNotificationUri函數,這部分內容和ContentObserver
//有關,相關內容留到第8章再進行分析
c.setNotificationUri(getContext().getContentResolver(), uri);
}
return c;
}
~~~
上邊代碼列出的兩個關鍵點分別是:
- 調用SQLiteQueryBuilder的query函數得到一個Cursor類型的對象。
- 調用Cursor類型對象的setNotificationUri函數。從名字上看,是為該對象設置通知URI。和ContentObserver有關的內容留到
第8章再進行分析。
來看SQLiteQueryBuilder的query函數。
1. SQLiteQueryBuilder的query函數分析
**SQLiteQueryBuilder.java::query**
~~~
public Cursor query(SQLiteDatabase db, String[]projectionIn,
String selection, String[] selectionArgs, String groupBy,
String having, String sortOrder, String limit) {
......
//調用buildQuery函數得到對應SQL語句的字符串
String sql= buildQuery(
projectionIn, selection, groupBy, having,
sortOrder, limit);
/*
調用SQLiteDatbase的rawQueryWithFactory函數,mFactory是SQLiteQueryBuilder
的成員變量,初始值為null,本例也沒有設置它,故mFactory為null
*/
returndb.rawQueryWithFactory(
mFactory, sql, selectionArgs,
SQLiteDatabase.findEditTable(mTables));
}
~~~
**SQLiteDatabase.java::rawQueryWithFactory**
~~~
public Cursor rawQueryWithFactory(
CursorFactory cursorFactory, String sql, String[] selectionArgs,
StringeditTable) {
verifyDbIsOpen();
BlockGuard.getThreadPolicy().onReadFromDisk();
//數據庫開發中經常碰到連接池的概念,其目的也是緩存重型資源。有興趣的讀者不妨自行
//研究一下面這個getDbConnection函數
SQLiteDatabase db = getDbConnection(sql);
//創建一個SQLiteDirectCursorDriver對象
SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(
db, sql,editTable);
Cursorcursor = null;
try {
//調用SQLiteCursorDriver的query函數
cursor = driver.query(
cursorFactory != null ? cursorFactory : mFactory,
selectionArgs);
}finally {
releaseDbConnection(db);
}
returncursor;
~~~
以上代碼中又出現一個新類型,即SQLiteCursorDriver,cursor變量是其query函數的返回值。
(1) SQLiteCursorDriver query函數分析
SQLiteCursorDriverquery函數的代碼如下:
**SQLiteCursorDriver.java::query**
~~~
public Cursor query(CursorFactory factory,String[] selectionArgs) {
//本例中,factory為空
SQLiteQuery query = null;
try {
mDatabase.lock(mSql);
mDatabase.closePendingStatements();
//①構造一個SQLiteQuery對象
query = new SQLiteQuery(mDatabase, mSql, 0, selectionArgs);
//原來,最后返回的游標對象其真實類型是SQLiteCursor
if(factory == null) {//②構造一個SQLiteCursor對象
mCursor = new SQLiteCursor(this, mEditTable, query);
}else {
mCursor =factory.newCursor(mDatabase, this,
mEditTable, query);
}
mQuery = query;
query = null;
return mCursor;//原來返回的cursor對象其真實類型是SQLiteCursor
}finally {
if (query != null) query.close();
mDatabase.unlock();
}
}
~~~
SQLiteCursorDriver的query函數的主要功能就是創建一個SQLiteQuery實例和一個SQLiteCursor實例。至此,我們終于搞清楚了MediaProvider 的query返回的游標對象其真實類型是SQLiteCursor。
* * * * *
**注意**:這里的MediaProvider query指的是圖7-5中的編號為8關鍵調用。
* * * * *
下面來看SQLiteQuery和SQLiteCursor為何方神圣。
(2) SQLiteQuery介紹
在圖7-4中曾介紹過SQLiteQuery,它保存了和查詢(即SQL的SELECT命令)命令相關的信息,其構造函數的代碼如下:
**SQLiteQuery.java::構造函數**
~~~
SQLiteQuery(SQLiteDatabase db, String query, intoffsetIndex,
String[] bindArgs) {
//注意,在本例中offsetIndex為0,offsetIndex的意義到后面再解釋
super(db,query);//調用基類構造函數
mOffsetIndex = offsetIndex;
bindAllArgsAsStrings(bindArgs);
}
~~~
SQLiteQuery的基類是SQLiteProgram,其代碼如下:
**SQLiteProgram.java::構造函數**
~~~
SQLiteProgram(SQLiteDatabase db, String sql) {
this(db,sql, null, true);//調用另外一個構造函數,注意傳遞的參數
}
SQLiteProgram(SQLiteDatabase db, String sql,Object[] bindArgs,
boolean compileFlag) {
//本例中bindArgs為null,compileFlag為true
mSql =sql.trim();
//返回該SQL語句的類型,query語句將返回STATEMENT_SELECT類型
int n =DatabaseUtils.getSqlStatementType(mSql);
switch(n) {
caseDatabaseUtils.STATEMENT_UPDATE:
mStatementType = n | STATEMENT_CACHEABLE;
break;
caseDatabaseUtils.STATEMENT_SELECT:
/*
mStatementType成員變量用于標示該SQL語句的類型,如果該SQL語句
是STATEMENT_SELECT類型,則mStatementType將設置STATEMENT_CACHEABLE
標志。該標志表示此對象將被緩存起來,以避免再次執行同樣的SELECT命令時重新構造
一個對象
*/
mStatementType = n | STATEMENT_CACHEABLE |
STATEMENT_USE_POOLED_CONN;
break;
......//其他情況處理
default:
mStatementType = n;
}
db.acquireReference();//增加引用計數
db.addSQLiteClosable(this);
mDatabase= db;
nHandle =db.mNativeHandle;//此mNativeHandle對應一個SQLite3實例
if(bindArgs != null) ......//綁定參數
//complieAndBindAllArgs將為此對象綁定一個sqlite3_stmt實例,native層對象的指針
//保存在nStatement成員變量中
if(compileFlag) compileAndbindAllArgs();
}
~~~
來看compileAndbindAllArgs函數,其代碼是:
**SQLiteProgram.java::compileAndbindAllArgs**
~~~
void compileAndbindAllArgs() {
......
//如果該對象還未和native層的sqlite3_stmt實例綁定,則調用compileSql函數
if(nStatement == 0) compileSql();
......//綁定參數
for(int index : mBindArgs.keySet()) {
Object value = mBindArgs.get(index);
if (value == null) {
native_bind_null(index);
}......//綁定其他類型的數據
}
}
~~~
compileSql函數將綁定Java層SQLiteQuery對象到一個Native的sqlite3_stmt實例。根據前文的分析,這個綁定是通過SQLiteCompileSql對象實施的,其相關代碼如下:
**SQLiteProgram.java::compileSql**
~~~
private void compileSql() {
//如果mStatementType未設置STATEMENT_CACHEABLE標志,則每次都創建一個
// SQLiteCompiledSql對象。根據7.3.2節小標題3中的分析,該對象會真正和native層的
//sqlite_stmt實例綁定
if((mStatementType & STATEMENT_CACHEABLE) == 0) {
mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
nStatement = mCompiledSql.nStatement;
return;
}
//從SQLiteDatabase對象中查詢是否已經緩存過符合該SQL語句要求的SQLiteCompiledSql
//對象
mCompiledSql = mDatabase.getCompiledStatementForSql(mSql);
if(mCompiledSql == null) {
//創建一個新的SQLiteCompiledSql對象,并把它保存到mDatabase中
mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
mCompiledSql.acquire();
mDatabase.addToCompiledQueries(mSql, mCompiledSql);
} ......
//保存Native層sqlite3_stmt實例指針到nStatement成員變量
nStatement= mCompiledSql.nStatement;
}
~~~
通過以上分析可以發現,SQLiteQuery將和一個代表SELECT命令的sqlite3_stmt實例綁定。同時,為了減少創建sqlite3_stmt實例的開銷,SQLiteDatabase框架還會把對應的SQL語句和對應的SQLiteCompiledSql對象緩存起來。如果下次執行同樣的SELECT語句,那么系統將直接取出之前保存的SQLiteCompiledSql對象,這樣就不用重新創建sqlite3_stmt實例了。
* * * * *
**思考**:與直接使用SQLite API相比,SQLiteDatabase框架明顯考慮了更多問題。不知讀者自己封裝C++ SQLite庫時是否想到了這些問題?
* * * * *
(3) SQLiteCursor分析
再來看SQLiteCursor類,其構造函數的代碼如下:
**SQLiteCursor.java::構造函數**
~~~
public SQLiteCursor(SQLiteCursorDriver driver,String editTable,
SQLiteQuery query) {
......
mDriver =driver;
mEditTable = editTable;
mColumnNameMap = null;
mQuery =query;//保存此SQLiteQuery對象
query.mDatabase.lock(query.mSql);
try {
//得到此次執行query得到的結果集所包含的列數
intcolumnCount = mQuery.columnCountLocked();
mColumns = new String[columnCount];
for(int i = 0; i < columnCount; i++) {
String columnName = mQuery.columnNameLocked(i);
//保存列名
mColumns[i] = columnName;
if ("_id".equals(columnName)) {
mRowIdColumnIndex = i;//保存“_id”列在結果集中的索引位置
}
}
}finally {
query.mDatabase.unlock();
}
}
~~~
SQLiteCursor比較簡單,此處不再詳述。
2. Cursor分析
至此,我們已經知道MediaProvider query返回的游標對象的真實類型了。現在,終于可以請Cursor家族登臺亮相了,如圖7-6所示。
:-: 
圖7-6 Cursor家族
圖7-6中元素較多,包含的知識點也較為復雜,因此必須仔細閱讀下文的解釋。
通過7.3.1節中SQLiteTest示例可知,query查詢(即SELECT命令)的結果和一個Native sqlite_stmt實例綁定在一起,開發者可通過遍歷該sqlite_stmt實例得到自己想要的結果(例如,調用sqlite3_step遍歷結果集中的行,然后通過sqlite3_column_xxx取出指定列的值)。查詢結果集可通過數據庫開發技術中一個專用術語——游標(Cursor)來遍歷和獲取。圖7-6的左上部分是和Cursor有關的類,它們包括:接口類Cursor和CrossProcessCursor、抽象類AbstractCursor、AbstractWindowCursor,以及真正的實現類SQliteCursor。根據前面的分析,SQLiteCursor內部保存一個已經綁定了sqlite3_stmt實例的SQLiteQuery對象,故讀者可簡單地把SQLiteCursor看成是一個已經包含了查詢結果集的游標對象,雖然此時還并未真正執行SQL語句。
如上所述,SQLiteCursor是一個已經包含了結果集的游標對象。從進程角度看,query的結果集目前還屬于MediaProvider所在的進程,而本次query請求是由客戶端發起的,所以一定要有一種方法將MediaProvider中的結果集傳遞到客戶端進程。數據傳遞使用的技術很簡單,就是大家耳熟能詳的共享內存技術。SQLite API沒有提供相關的功能,但是SQLiteDatabase框架對跨進程數據傳遞進行了封裝,最終得到了圖7-6左上部分的CursorWindow類。其代碼中的注釋明確表明了CursorWindow的作用:它是“A buffer containing multiplecursor rows”。
認識了CursorWindow,相信讀者也能猜出query中數據傳遞的大致流程了。
MediaProvider將結果集中的數據存儲到CursorWindow的共享內存中,然后客戶端將其從共享內存中取出來即可。
上述流程描述是對的,但實際過程并非如此簡單,因為SQLiteDatabase框架希望客戶端看到的不是共享內存,而是一個代表結果集的游標對象,就好像客戶端查詢的是本進程中的數據庫一樣。由于存在這種要求[^①],Android構造了圖7-6右下角的類家族。
其中,最重要的兩個類是CursorToBulkCursorAdaptor和BulkCursorToCursorAdatpor。從名字上看,它們采用了設計模式中的Adaptor模式;從繼承關系上看,這兩個類將參與跨進程的Binder通信(其中客戶端使用的BulkCursorToCursorAdaptor通過mBulkCursor與位于MediaProvider所在進程的CursorToBulkCursorAdaptor通信)。這兩個類中最重要的是onMove函數,以后我們碰到時再作分析。
另外,圖7-6中右上角部分展示了CursorWrapperInner類的派生關系。CursorWrapperInner類是ContentResolver query函數最終返回給客戶端的游標對象的類型。CursorWrapperInner的目的應該是拓展CursorToBulkCursorAdaptor類的功能。
Cursor家族有些復雜。筆者覺得,目前對Cursor的架構設計有些過度(over-designed)。這不僅會導致我們分析時困難重重,并且也會對實際代碼的運行效率造成一定損失。
下面我們將焦點放到跨進程的數據傳輸上。
[^①]:此處應該還存在其他方面的設計考慮,希望讀者能參與討論。
- 前言
- 第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 本章小結