在Android系統(tǒng)中,不同的應(yīng)用程序是不能直接讀寫對(duì)方的數(shù)據(jù)文件的,如果它們想共享數(shù)據(jù)的話,只能通過(guò)Content Provider組件來(lái)實(shí)現(xiàn)。那么,Content Provider組件又是如何突破應(yīng)用程序邊界權(quán)限控制來(lái)實(shí)現(xiàn)在不同的應(yīng)用程序之間共享數(shù)據(jù)的呢?在前面的文章中,我們已經(jīng)簡(jiǎn)要介紹過(guò)它是通過(guò)Binder進(jìn)程間通信機(jī)制以及匿名共享內(nèi)存機(jī)制來(lái)實(shí)現(xiàn)的,在本文中,我們將詳細(xì)分析它的數(shù)據(jù)共享原理。
Android應(yīng)用程序之間不能直接訪問(wèn)對(duì)方的數(shù)據(jù)文件的障礙在于每一個(gè)應(yīng)用程序都有自己的用戶ID,而每一個(gè)應(yīng)用程序所創(chuàng)建的文件的讀寫權(quán)限都是只賦予給自己所屬的用戶,因此,就限制了應(yīng)用程序之間相互讀寫數(shù)據(jù)的操作,關(guān)于Android應(yīng)用程序的權(quán)限問(wèn)題,具體可以參考前面一篇文章Android應(yīng)用程序組件Content Provider簡(jiǎn)要介紹和學(xué)習(xí)計(jì)劃。通過(guò)前面Android進(jìn)程間通信(IPC)機(jī)制Binder簡(jiǎn)要介紹和學(xué)習(xí)計(jì)劃等一系列文章的學(xué)習(xí),我們知道,Binder進(jìn)程間通信機(jī)制可以突破了以應(yīng)用程序?yàn)檫吔绲臋?quán)限控制來(lái)實(shí)現(xiàn)在不同應(yīng)用程序之間傳輸數(shù)據(jù),而Content Provider組件在不同應(yīng)用程序之間共享數(shù)據(jù)正是基于Binder進(jìn)程間通信機(jī)制來(lái)實(shí)現(xiàn)的。雖然Binder進(jìn)程間通信機(jī)制突破了以應(yīng)用程序?yàn)檫吔绲臋?quán)限控制,但是它是安全可控的,因?yàn)閿?shù)據(jù)的訪問(wèn)接口是由數(shù)據(jù)的所有者來(lái)提供的,換句話來(lái)說(shuō),就是數(shù)據(jù)提供方可以在接口層來(lái)實(shí)現(xiàn)安全控制,決定哪些數(shù)據(jù)是可以讀,哪些數(shù)據(jù)可以寫。雖然Content Provider組件本身也提供了讀寫權(quán)限控制,但是它的控制粒度是比較粗的,如果有需要,我們還是可以在接口訪問(wèn)層做更細(xì)粒度的權(quán)限控制以達(dá)到數(shù)據(jù)安全的目的。
Binder進(jìn)程間通信機(jī)制雖然打通了應(yīng)用程序之間共享數(shù)據(jù)的通道,但是還有一個(gè)問(wèn)題需要解決,那就是數(shù)據(jù)要以什么來(lái)作來(lái)媒介來(lái)傳輸。我們知道,應(yīng)用程序采用Binder進(jìn)程間通信機(jī)制進(jìn)行通信時(shí),要傳輸?shù)臄?shù)據(jù)都是采用函數(shù)參數(shù)的形式進(jìn)行的,對(duì)于一般的進(jìn)程間調(diào)來(lái)來(lái)說(shuō),這是沒(méi)有問(wèn)題的,然而,對(duì)于應(yīng)用程序之間的共享數(shù)據(jù)來(lái)說(shuō),它們的數(shù)據(jù)量可能是非常大的,如果還是簡(jiǎn)單的用函數(shù)參數(shù)的形式來(lái)傳遞,效率就會(huì)比較低下。通過(guò)前面Android系統(tǒng)匿名共享內(nèi)存Ashmem(Anonymous Shared Memory)簡(jiǎn)要介紹和學(xué)習(xí)計(jì)劃等一系列文章的學(xué)習(xí),我們知道,在應(yīng)用程序進(jìn)程之間以匿名共享內(nèi)存的方式來(lái)傳輸數(shù)據(jù)效率是非常高的,因?yàn)樗鼈冎g只需要傳遞一個(gè)文件描述符就可以了。因此,Content Provider組件在不同應(yīng)用程序之間傳輸數(shù)據(jù)正是基于匿名共享內(nèi)存機(jī)制來(lái)實(shí)現(xiàn)的。
在繼續(xù)分析Content Provider組件在不同應(yīng)用程序之間共享數(shù)據(jù)的原理之前,我們假設(shè)應(yīng)用程序之間需要共享的數(shù)據(jù)是保存在數(shù)據(jù)庫(kù)(SQLite)中的,因此,接下來(lái)的分析都是基于SQLite數(shù)據(jù)庫(kù)游標(biāo)(SQLiteCursor)來(lái)進(jìn)行的。SQLiteCursor在共享數(shù)據(jù)的傳輸過(guò)程中發(fā)揮著重要的作用,因此,我們先來(lái)它和其它相關(guān)的類的關(guān)系圖,如下所示:

首先在第三方應(yīng)用程序這一側(cè),當(dāng)它需要訪問(wèn)Content Provider中的數(shù)據(jù)時(shí),它會(huì)在本進(jìn)程中創(chuàng)建一個(gè)CursorWindow對(duì)象,它在內(nèi)部創(chuàng)建了一塊匿名共享內(nèi)存,同時(shí),它實(shí)現(xiàn)了Parcel接口,因此它可以在進(jìn)程間傳輸。接下來(lái)第三方應(yīng)用程序把這個(gè)CursorWindow對(duì)象(連同它內(nèi)部的匿名共享內(nèi)存文件描述符)通過(guò)Binder進(jìn)程間調(diào)用傳輸?shù)紺ontent Provider這一側(cè)。這個(gè)匿名共享內(nèi)存文件描述符傳輸?shù)紹inder驅(qū)動(dòng)程序的時(shí)候,Binder驅(qū)動(dòng)程序就會(huì)在目標(biāo)進(jìn)程(即Content Provider所在的進(jìn)程)中創(chuàng)建另一個(gè)匿名共享文件描述符,指向前面已經(jīng)創(chuàng)建好的匿名共享內(nèi)存,因此,就實(shí)現(xiàn)了在兩個(gè)進(jìn)程中共享同一塊匿名內(nèi)存,這個(gè)過(guò)程具體可以參考Android系統(tǒng)匿名共享內(nèi)存Ashmem(Anonymous Shared Memory)在進(jìn)程間共享的原理分析一文。
在Content Provider這一側(cè),利用在Binder驅(qū)動(dòng)程序?yàn)樗鼊?chuàng)建好的這個(gè)匿名共享內(nèi)存文件描述符,在本進(jìn)程中創(chuàng)建了一個(gè)CursorWindow對(duì)象。現(xiàn)在,Content Provider開(kāi)始要從本地中從數(shù)據(jù)庫(kù)中查詢第三方應(yīng)用程序想要獲取的數(shù)據(jù)了。Content Provider首先會(huì)創(chuàng)建一個(gè)SQLiteCursor對(duì)象,即SQLite數(shù)據(jù)庫(kù)游標(biāo)對(duì)象,它繼承了AbstractWindowedCursor類,后者又繼承了AbstractCursor類,而AbstractCursor類又實(shí)現(xiàn)了CrossProcessCursor和Cursor接口。其中,最重要的是在AbstractWindowedCursor類中,有一個(gè)成員變量mWindow,它的類型為CursorWindow,這個(gè)成員變量是通過(guò)AbstractWindowedCursor的子類SQLiteCursor的setWindow成員函數(shù)來(lái)設(shè)置的。這個(gè)SQLiteCursor對(duì)象設(shè)置好了父類AbstractWindowedCursor類的mWindow成員變量之后,它就具有傳輸數(shù)據(jù)的能力了,因?yàn)檫@個(gè)mWindow對(duì)象內(nèi)部包含一塊匿名共享內(nèi)存。此外,這個(gè)SQLiteCursor對(duì)象的內(nèi)部有兩個(gè)成員變量,一個(gè)是SQLite數(shù)據(jù)庫(kù)對(duì)象mDatabase,另外一個(gè)是SQLite數(shù)據(jù)庫(kù)查詢對(duì)象mQuery。SQLite數(shù)據(jù)庫(kù)查詢對(duì)象mQuery的類型為SQLiteQuery,它繼承了SQLiteProgram類,后者又繼承了SQLiteClosable類。SQLiteProgram類代表一個(gè)數(shù)據(jù)庫(kù)存查詢計(jì)劃,它的成員變量mCompiledSql包含了一個(gè)已經(jīng)編譯好的SQL查詢語(yǔ)句,SQLiteCursor對(duì)象就是利用這個(gè)編譯好的SQL查詢語(yǔ)句來(lái)獲得數(shù)據(jù)的,但是它并不是馬上就去獲取數(shù)據(jù)的,而是等到需要時(shí)才去獲取。
那么,要等到什么時(shí)候才會(huì)需要獲取數(shù)據(jù)呢?一般來(lái)說(shuō),如果第三方應(yīng)用程序在請(qǐng)求Content Provider返回?cái)?shù)據(jù)時(shí),如果指定了要返回關(guān)于這些數(shù)據(jù)的元信息時(shí),例如數(shù)據(jù)條目的數(shù)量,那么Content Provider在把這個(gè)SQLiteCursor對(duì)象返回給第三方應(yīng)用程序之前,就會(huì)去獲取數(shù)據(jù),因?yàn)橹挥蝎@取了數(shù)據(jù)之后,才知道數(shù)據(jù)條目的數(shù)量是多少。SQLiteCursor對(duì)象通過(guò)調(diào)用成員變量mQuery的fillWindow成員函數(shù)來(lái)把從SQLite數(shù)據(jù)庫(kù)中查詢得到的數(shù)據(jù)保存其父類AbstractWindowedCursor的成員變量mWindow中去,即保存到第三方應(yīng)用程序創(chuàng)建的這塊匿名共享內(nèi)存中去。如果第三方應(yīng)用程序在請(qǐng)求Content Provider返回?cái)?shù)據(jù)時(shí),沒(méi)有指定要返回關(guān)于這些數(shù)據(jù)的元信息,那么,就要等到第三方應(yīng)用程序首次調(diào)用這個(gè)從Content Provider處返回的SQLiteCursor對(duì)象的數(shù)據(jù)獲取方法時(shí),才會(huì)真正執(zhí)行從數(shù)據(jù)庫(kù)存中查詢數(shù)據(jù)的操作,例如調(diào)用了SQLiteCursor對(duì)象的getCount或者moveToFirst成員函數(shù)時(shí)。這是一種數(shù)據(jù)懶加載機(jī)制,需要的時(shí)候才去加載,這樣就提高了數(shù)據(jù)傳輸過(guò)程中的效率。
上面說(shuō)到,Content Provider向第三方應(yīng)用程序返回的數(shù)據(jù)實(shí)際上是一個(gè)SQLiteCursor對(duì)象,那么,這個(gè)SQLiteCursor對(duì)象是如何傳輸?shù)降谌綉?yīng)用程序的呢?因?yàn)樗旧聿⒉皇且粋€(gè)Binder對(duì)象,我們需要對(duì)它進(jìn)行適配一下。首先,Content Provider會(huì)根據(jù)這個(gè)SQLiteCursor對(duì)象來(lái)創(chuàng)建一個(gè)CursorToBulkCursorAdaptor適配器對(duì)象,這個(gè)適配器對(duì)象是一個(gè)Binder對(duì)象,因此,它可以在進(jìn)程間傳輸,同時(shí),它實(shí)現(xiàn)了IBulkCursor接口。Content Provider接著就通過(guò)Binder進(jìn)程間通信機(jī)制把這個(gè)CursorToBulkCursorAdaptor對(duì)象返回給第三方應(yīng)用程序,第三方應(yīng)用程序得到了這個(gè)CursorToBulkCursorAdaptor之后,再在本地創(chuàng)建一個(gè)BulkCursorToCursorAdaptor對(duì)象,這個(gè)BulkCursorToCursorAdaptor對(duì)象的繼承結(jié)構(gòu)和SQLiteCursor對(duì)象是一樣的,不過(guò),它沒(méi)有設(shè)置父類AbstractWindowedCursor的mWindow成員變量,因此,它只可以通過(guò)它內(nèi)部的CursorToBulkCursorAdaptor對(duì)象引用來(lái)訪問(wèn)匿名共享內(nèi)存中的數(shù)據(jù),即通過(guò)訪問(wèn)Content Provider這一側(cè)的SQLiteCursor對(duì)象來(lái)訪問(wèn)共享數(shù)據(jù)。
上面描述的數(shù)據(jù)共享模型還是比較復(fù)雜的,一下子理解不了也不要緊,下面我們還會(huì)結(jié)合第三方應(yīng)用程序和Content Provider傳輸共享數(shù)據(jù)的完整過(guò)程來(lái)進(jìn)一步分析Content Provider的數(shù)據(jù)共享原理,到時(shí)候再回過(guò)頭來(lái)看這個(gè)數(shù)據(jù)共享模型就會(huì)清晰很多了。在接下來(lái)的內(nèi)容中,我們就繼續(xù)以Android應(yīng)用程序組件Content Provider應(yīng)用實(shí)例一文的例子來(lái)分析Content Provider在不同應(yīng)用程序之間共享數(shù)據(jù)的原理。在Android應(yīng)用程序組件Content Provider應(yīng)用實(shí)例這篇文章介紹的應(yīng)用程序Article中,它的主窗口MainActivity是通過(guò)調(diào)用它的內(nèi)部ArticlesAdapter對(duì)象的getArticleByPos成員函數(shù)來(lái)把ArticlesProvider中的文章信息條目一條一條地取回來(lái)顯示在ListView中的,在這篇文章中,我們就從ArticlesAdapter類的getArticleByPos函數(shù)開(kāi)始,一步一步地分析第三方應(yīng)用程序Article從ArticlesProvider這個(gè)Content Provider中獲取數(shù)據(jù)的過(guò)程。同樣,我們先來(lái)看看這個(gè)過(guò)程的序列圖,然后再詳細(xì)分析每一個(gè)步驟:

Step 1. ArticlesAdapter.getArticleByPos
這個(gè)函數(shù)定義在前面一篇文章Android應(yīng)用程序組件Content Provider應(yīng)用實(shí)例介紹的應(yīng)用程序Artilce源代碼工程目錄下,在文件為packages/experimental/Article/src/shy/luo/article/ArticlesAdapter.java中:
public class ArticlesAdapter {
......
private ContentResolver resolver = null;
public ArticlesAdapter(Context context) {
resolver = context.getContentResolver();
}
......
public Article getArticleByPos(int pos) {
Uri uri = ContentUris.withAppendedId(Articles.CONTENT_POS_URI, pos);
String[] projection = new String[] {
Articles.ID,
Articles.TITLE,
Articles.ABSTRACT,
Articles.URL
};
Cursor cursor = resolver.query(uri, projection, null, null, Articles.DEFAULT_SORT_ORDER);
if (!cursor.moveToFirst()) {
return null;
}
int id = cursor.getInt(0);
String title = cursor.getString(1);
String abs = cursor.getString(2);
String url = cursor.getString(3);
return new Article(id, title, abs, url);
}
}
這個(gè)函數(shù)通過(guò)應(yīng)用程序上下文的ContentResolver接口resolver的query函數(shù)來(lái)獲得與Articles.CONTENT_POS_URI這個(gè)URI對(duì)應(yīng)的文章信息條目。常量Articles.CONTENT_POS_URI是在應(yīng)用程序ArticlesProvider中定義的,它的值為“content://shy.luo.providers.articles/pos”,通過(guò)調(diào)用ContentUris.withAppendedId函數(shù)來(lái)在后面添加了一個(gè)整數(shù),表示要獲取指定位置的文章信息條目。這個(gè)位置是指
ArticlesProvider這個(gè)Content Provider中的所有文章信息條目按照Articles.DEFAULT_SORT_ORDER來(lái)排序后得到的位置的,常量Articles.DEFAULT_SORT_ORDER也是在應(yīng)用程序ArticlesProvider中定義的,它的值為“_id asc”,即按照文章信息的ID值從小到大來(lái)排列。
Step 2. ContentResolver.query
這個(gè)函數(shù)定義在frameworks/base/core/java/android/content/ContentResolver.java文件中:
public abstract class ContentResolver {
......
public final Cursor query(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder) {
IContentProvider provider = acquireProvider(uri);
if (provider == null) {
return null;
}
try {
......
Cursor qCursor = provider.query(uri, projection, selection, selectionArgs, sortOrder);
......
return new CursorWrapperInner(qCursor, provider);
} catch (RemoteException e) {
......
} catch(RuntimeException e) {
......
}
}
......
}
這個(gè)函數(shù)首先通過(guò)調(diào)用acquireProvider函數(shù)來(lái)獲得與參數(shù)uri對(duì)應(yīng)的Content Provider接口,然后再通過(guò)這個(gè)接口的query函數(shù)來(lái)獲得相應(yīng)的數(shù)據(jù)。我們先來(lái)看看acquireProvider函數(shù)的實(shí)現(xiàn),再回過(guò)頭來(lái)分析這個(gè)Content Provider接口的query函數(shù)的實(shí)現(xiàn)。
Step 3. ContentResolver.acquireProvider
Step 4. ApplicationContentResolver.acquireProvider
Step 5. ActivityThread.acquireProvider
Step 6. ActivityThread.getProvider
從Step 3到Step 6是獲取與上面Step 2中傳進(jìn)來(lái)的參數(shù)uri對(duì)應(yīng)的Content Provider接口的過(guò)程。在前面一篇文章Android應(yīng)用程序組件Content Provider的啟動(dòng)過(guò)程源代碼分析中,我們已經(jīng)詳細(xì)介紹過(guò)這個(gè)過(guò)程了,這里不再詳述。不過(guò)這里我們假設(shè),這個(gè)Content Provider接口之前已經(jīng)創(chuàng)建好了,因此,在Step 6的ActivityThread.getProvider函數(shù)中,通過(guò)getExistingProvider函數(shù)就把之前已經(jīng)好的Content Provider接口返回來(lái)了。
回到Step 2中的ContentResolver.query函數(shù)中,它繼續(xù)調(diào)用這個(gè)返回來(lái)的Content Provider接口來(lái)獲取數(shù)據(jù)。從這篇文章Android應(yīng)用程序組件Content Provider的啟動(dòng)過(guò)程源代碼分析中,我們知道,這個(gè)Content Provider接口實(shí)際上是一個(gè)在ContentProvider類的內(nèi)部所創(chuàng)建的一個(gè)Transport對(duì)象的遠(yuǎn)程接口。這個(gè)Transport類繼承了ContentProviderNative類,是一個(gè)Binder對(duì)象的Stub類,因此,接下來(lái)就會(huì)進(jìn)入到這個(gè)Binder對(duì)象的Proxy類ContentProviderProxy中執(zhí)行query函數(shù)。
Step 7. ContentProviderProxy.query
這個(gè)函數(shù)定義在frameworks/base/core/java/android/content/ContentProviderNative.java文件中:
final class ContentProviderProxy implements IContentProvider {
......
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder) throws RemoteException {
//TODO make a pool of windows so we can reuse memory dealers
CursorWindow window = new CursorWindow(false /* window will be used remotely */);
BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
IBulkCursor bulkCursor = bulkQueryInternal(
url, projection, selection, selectionArgs, sortOrder,
adaptor.getObserver(), window,
adaptor);
if (bulkCursor == null) {
return null;
}
return adaptor;
}
......
}
這個(gè)函數(shù)首先會(huì)創(chuàng)建一個(gè)CursorWindow對(duì)象,前面已經(jīng)說(shuō)過(guò),這個(gè)CursorWindow對(duì)象包含了一塊匿名共享內(nèi)存,它的作用是把這塊匿名共享內(nèi)存通過(guò)Binder進(jìn)程間通信機(jī)制傳給Content Proivder,好讓Content Proivder在里面返回所請(qǐng)求的數(shù)據(jù)。下面我們就先看看這個(gè)CursorWindow對(duì)象的創(chuàng)建過(guò)程,重點(diǎn)關(guān)注它是如何在內(nèi)部創(chuàng)建匿名共享內(nèi)存的,然后再回過(guò)頭來(lái)看看它調(diào)用bulkQueryInternal函數(shù)來(lái)做了些什么事情。
CursorWindow類定義在frameworks/base/core/java/android/database/CursorWindow.java文件中,我們來(lái)看看它的構(gòu)造函數(shù)的實(shí)現(xiàn):
public class CursorWindow extends SQLiteClosable implements Parcelable {
......
private int nWindow;
......
public CursorWindow(boolean localWindow) {
......
native_init(localWindow);
}
......
}
它主要調(diào)用本地方法native_init來(lái)執(zhí)行初始化的工作,主要就是創(chuàng)建匿名共享內(nèi)存了,傳進(jìn)來(lái)的參數(shù)localWindow為false,表示這個(gè)匿名共享內(nèi)存只能通過(guò)遠(yuǎn)程調(diào)用來(lái)訪問(wèn),即前面我們所說(shuō)的,通過(guò)Content Proivder返回來(lái)的Cursor接口來(lái)訪問(wèn)這塊匿名共享內(nèi)存里面的數(shù)據(jù)。
Step 8. CursorWindow.native_init
這是一個(gè)JNI方法,它對(duì)應(yīng)定義在frameworks/base/core/jni/android_database_CursorWindow.cpp文件中的native_init_empty函數(shù):
static JNINativeMethod sMethods[] =
{
/* name, signature, funcPtr */
{"native_init", "(Z)V", (void *)native_init_empty},
......
};
函數(shù)native_init_empty的定義如下所示:
static void native_init_empty(JNIEnv * env, jobject object, jboolean localOnly)
{
......
CursorWindow * window;
window = new CursorWindow(MAX_WINDOW_SIZE);
......
if (!window->initBuffer(localOnly)) {
......
}
......
SET_WINDOW(env, object, window);
}
這個(gè)函數(shù)在C++層創(chuàng)建了一個(gè)CursorWindow對(duì)象,然后通過(guò)調(diào)用SET_WINDOW宏來(lái)把這個(gè)C++層的CursorWindow對(duì)象與Java層的CursorWindow對(duì)象關(guān)系起來(lái):
#define SET_WINDOW(env, object, window) (env->SetIntField(object, gWindowField, (int)window))
這里的gWindowField即定義為Java層的CursorWindow對(duì)象中的nWindow成員變量:
static jfieldID gWindowField;
......
int register_android_database_CursorWindow(JNIEnv * env)
{
jclass clazz;
clazz = env->FindClass("android/database/CursorWindow");
......
gWindowField = env->GetFieldID(clazz, "nWindow", "I");
......
}
這種用法在Android應(yīng)用程序框架層中非常普遍。
下面我們重點(diǎn)關(guān)注C++層的CursorWindow對(duì)象的initBuffer函數(shù)的實(shí)現(xiàn)。
Step 9. CursorWindow.initBuffer
C++層的CursorWindow類定義在frameworks/base/core/jni/CursorWindow.cpp文件中:
bool CursorWindow::initBuffer(bool localOnly)
{
......
sp<MemoryHeapBase> heap;
heap = new MemoryHeapBase(mMaxSize, 0, "CursorWindow");
if (heap != NULL) {
mMemory = new MemoryBase(heap, 0, mMaxSize);
if (mMemory != NULL) {
mData = (uint8_t *) mMemory->pointer();
if (mData) {
mHeader = (window_header_t *) mData;
mSize = mMaxSize;
......
}
}
......
} else {
......
}
}
這里我們就可以很清楚地看到,在CursorWindow類的內(nèi)部有一個(gè)成員變量mMemory,它的類型是MemoryBase。MemoryBase類為我們封裝了匿名共享內(nèi)存的訪問(wèn)以及在進(jìn)程間的傳輸?shù)葐?wèn)題,具體可以參考前面一篇文章
Android系統(tǒng)匿名共享內(nèi)存(Anonymous Shared Memory)C++調(diào)用接口分析,這里就不再詳述了。
通過(guò)Step 8和Step 9兩步,用來(lái)在第三方應(yīng)用程序和Content Provider之間傳輸數(shù)據(jù)的媒介就準(zhǔn)備好了,我們回到Step 7中,看看系統(tǒng)是如何把這個(gè)匿名共享存?zhèn)鬟f給Content Provider使用的。在Step 7中,最后調(diào)用bulkQueryInternal函數(shù)來(lái)進(jìn)一步操作。
Step 10. ContentProviderProxy.bulkQueryInternal
這個(gè)函數(shù)定義在frameworks/base/core/java/android/content/ContentProviderNative.java文件中:
final class ContentProviderProxy implements IContentProvider
{
......
private IBulkCursor bulkQueryInternal(
Uri url, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
IContentObserver observer, CursorWindow window,
BulkCursorToCursorAdaptor adaptor) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IContentProvider.descriptor);
url.writeToParcel(data, 0);
int length = 0;
if (projection != null) {
length = projection.length;
}
data.writeInt(length);
for (int i = 0; i < length; i++) {
data.writeString(projection[i]);
}
data.writeString(selection);
if (selectionArgs != null) {
length = selectionArgs.length;
} else {
length = 0;
}
data.writeInt(length);
for (int i = 0; i < length; i++) {
data.writeString(selectionArgs[i]);
}
data.writeString(sortOrder);
data.writeStrongBinder(observer.asBinder());
window.writeToParcel(data, 0);
// Flag for whether or not we want the number of rows in the
// cursor and the position of the "_id" column index (or -1 if
// non-existent). Only to be returned if binder != null.
final boolean wantsCursorMetadata = (adaptor != null);
data.writeInt(wantsCursorMetadata ? 1 : 0);
mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
DatabaseUtils.readExceptionFromParcel(reply);
IBulkCursor bulkCursor = null;
IBinder bulkCursorBinder = reply.readStrongBinder();
if (bulkCursorBinder != null) {
bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder);
if (wantsCursorMetadata) {
int rowCount = reply.readInt();
int idColumnPosition = reply.readInt();
if (bulkCursor != null) {
adaptor.set(bulkCursor, rowCount, idColumnPosition);
}
}
}
data.recycle();
reply.recycle();
return bulkCursor;
}
......
}
這個(gè)函數(shù)有點(diǎn)長(zhǎng),不過(guò)它的邏輯很簡(jiǎn)單,就是把查詢參數(shù)都寫到一個(gè)Parcel對(duì)象data中去,然后通過(guò)下面Binder進(jìn)程間通信機(jī)制把查詢請(qǐng)求傳給Content Provider處理:
mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
從這個(gè)Binder調(diào)用返回以后,就會(huì)得到一個(gè)IBulkCursor接口,它是一個(gè)Binder引用,實(shí)際是指向在Content Provider這一側(cè)創(chuàng)建的一個(gè)CursorToBulkCursorAdaptor對(duì)象,后面我們將會(huì)看到。有了這個(gè)IBulkCursor接口之后,我們就可以通過(guò)Binder進(jìn)程間調(diào)用來(lái)訪問(wèn)從Content Provider中查詢得到的數(shù)據(jù)了。這個(gè)IBulkCursor接口最終最設(shè)置了上面Step 7中創(chuàng)建的BulkCursorToCursorAdaptor對(duì)象adaptor中去:
adaptor.set(bulkCursor, rowCount, idColumnPosition);
BulkCursorToCursorAdaptor類實(shí)現(xiàn)了Cursor接口,因此,我們可以通過(guò)Curosr接口來(lái)訪問(wèn)這些查詢得到的共享數(shù)據(jù)。
在前面把查詢參數(shù)寫到Parcel對(duì)象data中去的過(guò)程中,有兩個(gè)步驟是比較重要的,分別下面這段執(zhí)行語(yǔ)句:
window.writeToParcel(data, 0);
// Flag for whether or not we want the number of rows in the
// cursor and the position of the "_id" column index (or -1 if
// non-existent). Only to be returned if binder != null.
final boolean wantsCursorMetadata = (adaptor != null);
data.writeInt(wantsCursorMetadata ? 1 : 0);
調(diào)用window.writeToParcel是把window對(duì)象內(nèi)部的匿名共享內(nèi)存塊通過(guò)Binder進(jìn)程間通信機(jī)制傳輸給Content Provider來(lái)使用;而當(dāng)傳進(jìn)來(lái)的參數(shù)adaptor不為null時(shí),就會(huì)往data中寫入一個(gè)整數(shù)1,表示讓Content Provider把查詢得到數(shù)據(jù)的元信息一起返回來(lái),例如數(shù)據(jù)的行數(shù)、數(shù)據(jù)行的ID列的索引位置等信息,這個(gè)整數(shù)值會(huì)促使Content Provider把前面說(shuō)的IBulkCursor接口返回給第三方應(yīng)用程序之前,真正執(zhí)行一把數(shù)據(jù)庫(kù)查詢操作,后面我們將看到這個(gè)過(guò)程。
現(xiàn)在,我們重點(diǎn)來(lái)關(guān)注一下CursorWindow類的writeToParcel函數(shù),看看它是如何把它內(nèi)部的匿名共享內(nèi)存對(duì)象寫到數(shù)據(jù)流data中去的。
Step 11. CursorWindow.writeToParcel
這個(gè)函數(shù)定義在frameworks/base/core/java/android/database/CursorWindow.java文件中:
public class CursorWindow extends SQLiteClosable implements Parcelable {
......
public void writeToParcel(Parcel dest, int flags) {
......
dest.writeStrongBinder(native_getBinder());
......
}
......
}
這個(gè)函數(shù)最主要的操作就是往數(shù)據(jù)流dest寫入一個(gè)Binder對(duì)象,這個(gè)Binder對(duì)象是通過(guò)調(diào)用本地方法native_getBinder來(lái)得到的。
Step 12. CursorWindow.native_getBinder
這個(gè)函數(shù)定義在frameworks/base/core/jni/android_database_CursorWindow.cpp文件中:
static jobject native_getBinder(JNIEnv * env, jobject object)
{
CursorWindow * window = GET_WINDOW(env, object);
if (window) {
sp<IMemory> memory = window->getMemory();
if (memory != NULL) {
sp<IBinder> binder = memory->asBinder();
return javaObjectForIBinder(env, binder);
}
}
return NULL;
}
在前面的Step 8中,我們?cè)贑++層創(chuàng)建了一個(gè)CursorWindow對(duì)象,這個(gè)對(duì)象保存在Java層創(chuàng)建的CursorWindow對(duì)象的成員變量nWindow中,這里通過(guò)GET_WINDOW宏來(lái)把這個(gè)在C++層創(chuàng)建的CurosrWindow對(duì)象返回來(lái):
#define GET_WINDOW(env, object) ((CursorWindow *)env->GetIntField(object, gWindowField))
獲得了這個(gè)CursorWindow對(duì)象以后,就調(diào)用它的getMemory函數(shù)來(lái)獲得一個(gè)IMemory接口,這是一個(gè)Binder接口,具體可以參考前面一篇文章
Android系統(tǒng)匿名共享內(nèi)存(Anonymous Shared Memory)C++調(diào)用接口分析。
Step 13. CursorWindow.getMemory
這個(gè)函數(shù)定義在frameworks/base/core/jni/CursorWindow.h文件中:
class CursorWindow
{
public:
......
sp<IMemory> getMemory() {return mMemory;}
......
}
這個(gè)CursorWindow對(duì)象的成員變量mMemory就是在前面Step 9中創(chuàng)建的了。
這樣,在第三方應(yīng)用程序這一側(cè)創(chuàng)建的匿名共享存對(duì)象就可以傳遞給Content Provider來(lái)使用了。回到前面的Step 10中,所有的參數(shù)都就準(zhǔn)備就緒以后,就通過(guò)Binder進(jìn)程間通信機(jī)制把數(shù)據(jù)查詢請(qǐng)求發(fā)送給相應(yīng)的Content Proivder了。這個(gè)請(qǐng)求是在ContentProviderNative類的onTransact函數(shù)中響應(yīng)的。
Step 14. ContentProviderNative.onTransact
這個(gè)函數(shù)定義在frameworks/base/core/java/android/content/ContentProviderNative.java文件中:
abstract public class ContentProviderNative extends Binder implements IContentProvider {
......
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
try {
switch (code) {
case QUERY_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
Uri url = Uri.CREATOR.createFromParcel(data);
// String[] projection
int num = data.readInt();
String[] projection = null;
if (num > 0) {
projection = new String[num];
for (int i = 0; i < num; i++) {
projection[i] = data.readString();
}
}
// String selection, String[] selectionArgs...
String selection = data.readString();
num = data.readInt();
String[] selectionArgs = null;
if (num > 0) {
selectionArgs = new String[num];
for (int i = 0; i < num; i++) {
selectionArgs[i] = data.readString();
}
}
String sortOrder = data.readString();
IContentObserver observer = IContentObserver.Stub.
asInterface(data.readStrongBinder());
CursorWindow window = CursorWindow.CREATOR.createFromParcel(data);
// Flag for whether caller wants the number of
// rows in the cursor and the position of the
// "_id" column index (or -1 if non-existent)
// Only to be returned if binder != null.
boolean wantsCursorMetadata = data.readInt() != 0;
IBulkCursor bulkCursor = bulkQuery(url, projection, selection,
selectionArgs, sortOrder, observer, window);
reply.writeNoException();
if (bulkCursor != null) {
reply.writeStrongBinder(bulkCursor.asBinder());
if (wantsCursorMetadata) {
reply.writeInt(bulkCursor.count());
reply.writeInt(BulkCursorToCursorAdaptor.findRowIdColumnIndex(
bulkCursor.getColumnNames()));
}
} else {
reply.writeStrongBinder(null);
}
return true;
}
......
}
} catch (Exception e) {
DatabaseUtils.writeExceptionToParcel(reply, e);
return true;
}
return super.onTransact(code, data, reply, flags);
}
......
}
這一步其實(shí)就是前面Step 10的逆操作,把請(qǐng)求參數(shù)從數(shù)據(jù)流data中讀取出來(lái)。這里我們同樣是重點(diǎn)關(guān)注下面這兩個(gè)參數(shù)讀取的步驟:
CursorWindow window = CursorWindow.CREATOR.createFromParcel(data);
// Flag for whether caller wants the number of
// rows in the cursor and the position of the
// "_id" column index (or -1 if non-existent)
// Only to be returned if binder != null.
boolean wantsCursorMetadata = data.readInt() != 0;
通過(guò)調(diào)用CursorWindow.CREATOR.createFromParcel函數(shù)來(lái)從數(shù)據(jù)流data中重建一個(gè)本地的CursorWindow對(duì)象;接著又將數(shù)據(jù)流data的下一個(gè)整數(shù)值讀取出來(lái),如果這個(gè)整數(shù)值不為0,變量wantsCursorMetadata的值就為true,說(shuō)明Content Provider在返回IBulkCursor接口給第三方應(yīng)用程序之前,要先實(shí)際執(zhí)行一把數(shù)據(jù)庫(kù)查詢操作,以便把結(jié)果數(shù)據(jù)的元信息返回給第三方應(yīng)用程序。
通過(guò)下面的代碼我們可以看到,調(diào)用bulkQuery函數(shù)之后,就得到了一個(gè)IBulkCursor接口,這表示要返回的數(shù)據(jù)準(zhǔn)備就緒了,但是這時(shí)候?qū)嶋H上還沒(méi)有把結(jié)果數(shù)據(jù)從數(shù)據(jù)庫(kù)中提取出來(lái),而只是準(zhǔn)備好了一個(gè)SQL查詢計(jì)劃,等到真正要使用這些結(jié)果數(shù)據(jù)時(shí),系統(tǒng)才會(huì)真正執(zhí)行查詢數(shù)據(jù)庫(kù)的操作:
if (wantsCursorMetadata) {
reply.writeInt(bulkCursor.count());
......
}
在將這個(gè)IBulkCursor接口返回給第三方應(yīng)用程序之前,如果發(fā)現(xiàn)
wantsCursorMetadata的值就為true,就會(huì)調(diào)用它的count函數(shù)來(lái)獲得結(jié)果數(shù)據(jù)的總行數(shù),這樣就會(huì)導(dǎo)致系統(tǒng)真正去執(zhí)行數(shù)據(jù)庫(kù)查詢操作,并把結(jié)果數(shù)據(jù)保存到前面得到的CursorWindow對(duì)象中的匿名共享內(nèi)存中去。
下面我們就重點(diǎn)關(guān)注CursorWindow.CREATOR.createFromParcel函數(shù)是如何從數(shù)據(jù)流data中在本地構(gòu)造一個(gè)CursorWindow對(duì)象的。
Step 15. CursorWindow.CREATOR.createFromParcel
這個(gè)函數(shù)定義在frameworks/base/core/java/android/database/CursorWindow.java文件中:
public class CursorWindow extends SQLiteClosable implements Parcelable {
......
private CursorWindow(Parcel source) {
IBinder nativeBinder = source.readStrongBinder();
......
native_init(nativeBinder);
}
......
public static final Parcelable.Creator<CursorWindow> CREATOR
= new Parcelable.Creator<CursorWindow>() {
public CursorWindow createFromParcel(Parcel source) {
return new CursorWindow(source);
}
......
};
......
}
在創(chuàng)建CursorWindow對(duì)象的過(guò)程中,首先是從數(shù)據(jù)流source中將在前面Step 10中寫入的Binder接口讀取出來(lái),然后使用這個(gè)Binder接口來(lái)初始化這個(gè)CursorWindow對(duì)象,通過(guò)前面的Step 13,我們知道,這個(gè)Binder接口的實(shí)際類型為IMemory,它封裝了對(duì)匿名共享內(nèi)存的訪問(wèn)操作。初始化這個(gè)匿名共享內(nèi)存對(duì)象的操作是由本地方法native_init函數(shù)來(lái)實(shí)現(xiàn)的,下面我們就看看它的實(shí)現(xiàn)。
Step 16. CursorWindow.native_init
這個(gè)函數(shù)定義在frameworks/base/core/jni/android_database_CursorWindow.cpp文件中,對(duì)應(yīng)的函數(shù)為native_init_memory函數(shù):
static JNINativeMethod sMethods[] =
{
......
{"native_init", "(Landroid/os/IBinder;)V", (void *)native_init_memory},
};
函數(shù)native_init_memory的實(shí)現(xiàn)如下所示:
static void native_init_memory(JNIEnv * env, jobject object, jobject memObj)
{
sp<IMemory> memory = interface_cast<IMemory>(ibinderForJavaObject(env, memObj));
......
CursorWindow * window = new CursorWindow();
......
if (!window->setMemory(memory)) {
......
}
......
SET_WINDOW(env, object, window);
}
函數(shù)首先是將前面Step 15中傳進(jìn)來(lái)的Binder接口轉(zhuǎn)換為IMemory接口,接著創(chuàng)建一個(gè)C++層的CursorWindow對(duì)象,再接著用這個(gè)IMemory接口來(lái)初始化這個(gè)C++層的CursorWindow對(duì)象,最后像前面的Step 8一樣,通過(guò)宏SET_WINDOW把這個(gè)C++層的CursorWindow對(duì)象和前面在Step 15中創(chuàng)建的Java層CursorWindow對(duì)象關(guān)聯(lián)起來(lái)。
下面我們就重點(diǎn)關(guān)注CursorWindow類的setMemory函數(shù)的實(shí)現(xiàn),看看它是如何使用這個(gè)IMemory接口來(lái)初始化其內(nèi)部的匿名共享內(nèi)存對(duì)象的。
Step 17. CursorWindow.setMemory
這個(gè)函數(shù)定義在frameworks/base/core/jni/CursorWindow.cpp文件中:
bool CursorWindow::setMemory(const sp<IMemory>& memory)
{
mMemory = memory;
mData = (uint8_t *) memory->pointer();
......
mHeader = (window_header_t *) mData;
// Make the window read-only
ssize_t size = memory->size();
mSize = size;
mMaxSize = size;
mFreeOffset = size;
......
return true;
}
從前面一篇文章
Android系統(tǒng)匿名共享內(nèi)存(Anonymous Shared Memory)C++調(diào)用接口分析中,我們知道,這里得到的IMemory接口,實(shí)際上是一個(gè)Binder引用,它指向前面在Step 9中創(chuàng)建的MemoryBase對(duì)象,當(dāng)我們第一次調(diào)用這個(gè)接口的pointer函數(shù)時(shí),它便會(huì)通過(guò)Binder進(jìn)程間通信機(jī)制去請(qǐng)求這個(gè)MemoryBase對(duì)象把它內(nèi)部的匿名共享內(nèi)存文件描述符返回來(lái)給它,而Binder驅(qū)動(dòng)程序發(fā)現(xiàn)要傳輸?shù)氖且粋€(gè)文件描述符的時(shí)候,就會(huì)在目標(biāo)進(jìn)程中創(chuàng)建另外一個(gè)文件描述符,這個(gè)新建的文件描述符與要傳輸?shù)奈募枋龇赶虻氖峭粋€(gè)文件,在我們這個(gè)情景中,這個(gè)文件就是我們前面創(chuàng)建的匿名共享內(nèi)存文件了。因此,在目標(biāo)進(jìn)程中,即在Content Provider進(jìn)程中,它可以通過(guò)這個(gè)新建的文件描述符來(lái)訪問(wèn)這塊匿名共享內(nèi)存,這也是匿名共享內(nèi)存在進(jìn)程間的共享原理,具體可以參考另外一篇文章Android系統(tǒng)匿名共享內(nèi)存Ashmem(Anonymous Shared Memory)在進(jìn)程間共享的原理分析。
這樣,在Content Provider這一側(cè),就可以把第三方應(yīng)用程序請(qǐng)求的數(shù)據(jù)保存在這個(gè)匿名共享內(nèi)存中了,回到前面的Step 14中,下一步要執(zhí)行的函數(shù)便是bulkQuery了,它的作用為請(qǐng)求的數(shù)據(jù)制定好一個(gè)SQL數(shù)據(jù)庫(kù)查詢計(jì)劃。這個(gè)bulkQuery函數(shù)是由一個(gè)實(shí)現(xiàn)了IContentProvider接口的Binder對(duì)象來(lái)實(shí)現(xiàn)的,具體可以參考前面一篇文章Android應(yīng)用程序組件Content Provider的啟動(dòng)過(guò)程源代碼分析中,這個(gè)Binder對(duì)象的實(shí)際類型是定義在ContentProivder類內(nèi)部的Transport類。
Step 18. Transport.bulkQuery
這個(gè)函數(shù)定義在frameworks/base/core/java/android/content/ContentProvider.java文件中:
public abstract class ContentProvider implements ComponentCallbacks {
......
class Transport extends ContentProviderNative {
......
public IBulkCursor bulkQuery(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
IContentObserver observer, CursorWindow window) {
......
Cursor cursor = ContentProvider.this.query(uri, projection,
selection, selectionArgs, sortOrder);
......
return new CursorToBulkCursorAdaptor(cursor, observer,
ContentProvider.this.getClass().getName(),
hasWritePermission(uri), window);
}
......
}
......
}
這個(gè)函數(shù)主要做了兩件事情,一是調(diào)用ContentProvider的子類的query函數(shù)構(gòu)造一個(gè)數(shù)據(jù)庫(kù)查詢計(jì)劃,注意,從這個(gè)函數(shù)返回來(lái)的時(shí)候,還沒(méi)有真正執(zhí)行數(shù)據(jù)庫(kù)查詢的操作,而只是按照查詢條件準(zhǔn)備好了一個(gè)SQL語(yǔ)句,要等到第一次使用的時(shí)候才會(huì)去執(zhí)行數(shù)據(jù)庫(kù)查詢操作;二是使用前面一步得到的Cursor接口以及傳下來(lái)的參數(shù)window來(lái)創(chuàng)建一個(gè)CursorToBulkCursorAdaptor對(duì)象,這個(gè)對(duì)象實(shí)現(xiàn)了IBulkCursor接口,同時(shí)它也是一個(gè)Binder對(duì)象,是用來(lái)返回給第三方應(yīng)用程序使用的,第三方應(yīng)用程序必須通過(guò)這個(gè)接口來(lái)獲取從ContentProvider中查詢得到的數(shù)據(jù),而這個(gè)CursorToBulkCursorAdaptor對(duì)象的功能就是利用前面獲得的Cursor接口來(lái)執(zhí)行數(shù)據(jù)庫(kù)查詢操作,然后把查詢得到的結(jié)果保存在從參數(shù)傳下來(lái)的window對(duì)象內(nèi)部所引用的匿名共享內(nèi)存中去。我們先來(lái)看ContentProvider的子類的query函數(shù)的實(shí)現(xiàn),在我們這個(gè)情景中,這個(gè)子類就是ArticlesProvider了,然后再回過(guò)頭來(lái)看看這個(gè)CursorToBulkCursorAdaptor對(duì)象是如何把數(shù)據(jù)庫(kù)查詢計(jì)劃與匿名共享內(nèi)存關(guān)聯(lián)起來(lái)的。
Step 19. ArticlesProvider.query
這個(gè)函數(shù)定義在前面一篇文章Android應(yīng)用程序組件Content Provider應(yīng)用實(shí)例介紹的應(yīng)用程序ArtilcesProvider源代碼工程目錄下,在文件packages/experimental/ArticlesProvider/src/shy/luo/providers/articles/ArticlesProvider.java 中:
public class ArticlesProvider extends ContentProvider {
......
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
SQLiteQueryBuilder sqlBuilder = new SQLiteQueryBuilder();
String limit = null;
switch (uriMatcher.match(uri)) {
......
case Articles.ITEM_POS: {
String pos = uri.getPathSegments().get(1);
sqlBuilder.setTables(DB_TABLE);
sqlBuilder.setProjectionMap(articleProjectionMap);
limit = pos + ", 1";
break;
}
......
}
Cursor cursor = sqlBuilder.query(db, projection, selection, selectionArgs, null, null, TextUtils.isEmpty(sortOrder) ? Articles.DEFAULT_SORT_ORDER : sortOrder, limit);
......
return cursor;
}
......
}
從前面的Step 1中可以看到,傳進(jìn)來(lái)的參數(shù)uri的值為“content://shy.luo.providers.articles/pos”,通過(guò)uriMatcher的match函數(shù)來(lái)匹配這個(gè)uri的時(shí)候,得到的匹配碼為Articles.ITEM_POS,這個(gè)知識(shí)點(diǎn)可以參考前面這篇文章
Android應(yīng)用程序組件Content Provider應(yīng)用實(shí)例。因?yàn)槲覀兊臄?shù)據(jù)是保存在SQLite數(shù)據(jù)庫(kù)里面的,因此,必須要構(gòu)造一個(gè)SQL語(yǔ)句來(lái)將所請(qǐng)求的數(shù)據(jù)查詢出來(lái)。這里是通過(guò)SQLiteQueryBuilder類來(lái)構(gòu)造這個(gè)SQL查詢語(yǔ)句的,構(gòu)造好了以后,就調(diào)用它的query函數(shù)來(lái)準(zhǔn)備一個(gè)數(shù)據(jù)庫(kù)查詢計(jì)劃。
Step 20. SQLiteQueryBuilder.query
這個(gè)函數(shù)定義在frameworks/base/core/java/android/database/sqlite/SQLiteQueryBuilder.java文件中:
public class SQLiteQueryBuilder
{
......
public Cursor query(SQLiteDatabase db, String[] projectionIn,
String selection, String[] selectionArgs, String groupBy,
String having, String sortOrder, String limit) {
......
String sql = buildQuery(
projectionIn, selection, groupBy, having,
sortOrder, limit);
......
return db.rawQueryWithFactory(
mFactory, sql, selectionArgs,
SQLiteDatabase.findEditTable(mTables));
}
......
}
這里首先是調(diào)用buildQuery函數(shù)來(lái)構(gòu)造一個(gè)SQL語(yǔ)句,它無(wú)非就是根據(jù)從參數(shù)傳來(lái)列名子句、select子句、where子句、group by子句、having子句、order子句以及l(fā)imit子句來(lái)構(gòu)造一個(gè)完整的SQL子句,這些都是SQL語(yǔ)法的基礎(chǔ)知識(shí)了,這里我們就不關(guān)注了。構(gòu)造好這個(gè)SQL查詢語(yǔ)句之后,就調(diào)用從參數(shù)傳下來(lái)的數(shù)據(jù)庫(kù)對(duì)象db的rawQueryWithFactory函數(shù)來(lái)進(jìn)一步操作了。
Step 21. SQLiteDatabase.rawQueryWithFactory
這個(gè)函數(shù)定義在frameworks/base/core/java/android/database/sqlite/SQLiteDatabase.java文件中:
public class SQLiteDatabase extends SQLiteClosable {
......
public Cursor rawQueryWithFactory(
CursorFactory cursorFactory, String sql, String[] selectionArgs,
String editTable) {
......
SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable);
Cursor cursor = null;
try {
cursor = driver.query(
cursorFactory != null ? cursorFactory : mFactory,
selectionArgs);
} finally {
......
}
return cursor;
}
......
}
這個(gè)函數(shù)會(huì)在內(nèi)部創(chuàng)建一個(gè)SQLiteCursorDriver對(duì)象driver,然后調(diào)用它的query函數(shù)來(lái)創(chuàng)建一個(gè)Cursor對(duì)象,這個(gè)Cursor對(duì)象的實(shí)際類型是SQLiteCursor,下面我們將會(huì)看到,前面我們也已經(jīng)看到,這個(gè)
SQLiteCursor的內(nèi)部就包含了一個(gè)數(shù)據(jù)庫(kù)查詢計(jì)劃。
Step 22. SQLiteCursorDriver.query
這個(gè)函數(shù)定義在frameworks/base/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java文件中:
public class SQLiteDirectCursorDriver implements SQLiteCursorDriver {
......
public Cursor query(CursorFactory factory, String[] selectionArgs) {
// Compile the query
SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, 0, selectionArgs);
try {
......
// Create the cursor
if (factory == null) {
mCursor = new SQLiteCursor(mDatabase, this, mEditTable, query);
} else {
mCursor = factory.newCursor(mDatabase, this, mEditTable, query);
}
......
return mCursor;
} finally {
......
}
}
......
}
這里我們就可以清楚地看到,這個(gè)函數(shù)首先會(huì)根據(jù)數(shù)據(jù)庫(kù)對(duì)象mDatabase和原生SQL語(yǔ)句來(lái)構(gòu)造一個(gè)SQLiteQuery對(duì)象,這個(gè)對(duì)象的創(chuàng)建的過(guò)程中,就會(huì)解析這個(gè)原生SQL語(yǔ)句,并且創(chuàng)建好數(shù)據(jù)庫(kù)查詢計(jì)劃,這樣做的好處是等到真正查詢的時(shí)候就可以馬上從數(shù)據(jù)庫(kù)中獲得取數(shù)據(jù)了,而不用去分析和理解這個(gè)SQL字符串語(yǔ)句,這個(gè)就是所謂的SQL語(yǔ)句編譯了。有了這個(gè)SQLiteQuery對(duì)象之后,再把它和數(shù)據(jù)庫(kù)對(duì)象mDatabase等待信息一起來(lái)創(chuàng)建一個(gè)SQLiteCursor對(duì)象,于是,這個(gè)
SQLiteCursor對(duì)象就圈定要將來(lái)要從數(shù)據(jù)庫(kù)中獲取的數(shù)據(jù)了。這一步執(zhí)行完成之后,就把這個(gè)SQLiteCursor對(duì)象返回給上層,最終回到Step 18中的Transport類bulkQuery函數(shù)中。有了這個(gè)SQLiteCursor對(duì)象之后,就通過(guò)創(chuàng)建一個(gè)CursorToBulkCursorAdaptor對(duì)象來(lái)把它和匿名共享內(nèi)存關(guān)聯(lián)起來(lái),這樣,就為將來(lái)從數(shù)據(jù)庫(kù)中查詢得到的數(shù)據(jù)找到了歸宿。
CursorToBulkCursorAdaptor類定義在frameworks/base/core/java/android/database/CursorToBulkCursorAdaptor.java文件中,我們來(lái)看看它的對(duì)象的構(gòu)造過(guò)程,即它的構(gòu)造函數(shù)的實(shí)現(xiàn):
public final class CursorToBulkCursorAdaptor extends BulkCursorNative
implements IBinder.DeathRecipient {
......
public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer, String providerName,
boolean allowWrite, CursorWindow window) {
try {
mCursor = (CrossProcessCursor) cursor;
if (mCursor instanceof AbstractWindowedCursor) {
AbstractWindowedCursor windowedCursor = (AbstractWindowedCursor) cursor;
......
windowedCursor.setWindow(window);
} else {
......
}
} catch (ClassCastException e) {
......
}
......
}
......
}
這里傳進(jìn)來(lái)的參數(shù)cursor的類型為SQLiteCursor,從上面的類圖我們可以知道,
SQLiteCursor實(shí)現(xiàn)了CrossProcessCursor接口,并且繼承了AbstractWindowedCursor類,因此,上面第一個(gè)if語(yǔ)句的條件成立,于是就會(huì)把這個(gè)SQLiteCurosr對(duì)象轉(zhuǎn)換為一個(gè)AbstractWindowedCursor對(duì)象,目的是為了調(diào)用它的setWindow函數(shù)來(lái)把傳進(jìn)來(lái)的CursorWindow對(duì)象window保存起來(lái),以便后面用來(lái)保存數(shù)據(jù)。
Step 23. AbstractWindowedCursor.setWindow
這個(gè)函數(shù)定義在frameworks/base/core/java/android/database/AbstractWindowedCursor.java文件中:
public abstract class AbstractWindowedCursor extends AbstractCursor
{
......
public void setWindow(CursorWindow window) {
......
mWindow = window;
}
......
protected CursorWindow mWindow;
}
這個(gè)函數(shù)很簡(jiǎn)單,只是把參數(shù)window保存在
AbstractWindowedCursor類的成員變量mWindow中。注意,這個(gè)成員變量mWindow的訪問(wèn)權(quán)限為protected,即AbstractWindowedCursor的子類可以直接訪問(wèn)這個(gè)成員變量。
這一步完成以后,就返回到前面的Step 14中去了,執(zhí)行下面語(yǔ)句:
if (bulkCursor != null) {
reply.writeStrongBinder(bulkCursor.asBinder());
if (wantsCursorMetadata) {
reply.writeInt(bulkCursor.count());
reply.writeInt(BulkCursorToCursorAdaptor.findRowIdColumnIndex(
bulkCursor.getColumnNames()));
}
} else {
......
}
這里的bulkCursor不為null,于是,就會(huì)把這個(gè)
bulkCursor對(duì)象寫入到數(shù)據(jù)流reply中,這個(gè)接口是要通過(guò)Binder進(jìn)程間通信機(jī)制返回到第三方應(yīng)用程序的,它的實(shí)際類型就是我們?cè)谇懊?span style="font-family: Verdana, 'Lucida Grande', Arial, Helvetica, sans-serif; ">Step 18中創(chuàng)建的CursorToBulkCursorAdaptor對(duì)象了。
從前面的Step 14的分析中,我們知道,這里的布爾變量wantsCursorMetadata為true,于是就會(huì)把請(qǐng)求數(shù)據(jù)的行數(shù)以及數(shù)據(jù)行的ID列索引號(hào)一起寫入到數(shù)據(jù)流reply中去了。這里,我們重點(diǎn)分析IBulkCursor接口的count函數(shù),因?yàn)檫@個(gè)調(diào)用使得這個(gè)Content Provider會(huì)真正去執(zhí)行數(shù)據(jù)庫(kù)查詢的操作。至于是如何得到從數(shù)據(jù)庫(kù)查詢出來(lái)的數(shù)據(jù)行的ID列的位置呢?回憶前面這篇文章Android應(yīng)用程序組件Content Provider應(yīng)用實(shí)例,我們提到,如果我們想將數(shù)據(jù)庫(kù)表中的某一列作為數(shù)據(jù)行的ID列的話,那么就必須把這個(gè)列的名稱設(shè)置為"_id",這里的BulkCursorToCursorAdaptor類的靜態(tài)成員函數(shù)findRowIdColumnIndex函數(shù)就是根據(jù)這個(gè)列名"_id"來(lái)找到它是位于數(shù)據(jù)行的第幾列的。
CursorToBulkCursorAdaptor類定義在frameworks/base/core/java/android/database/CursorToBulkCursorAdaptor.java文件中,它的count成員函數(shù)的實(shí)現(xiàn)如下所示:
public final class CursorToBulkCursorAdaptor extends BulkCursorNative
implements IBinder.DeathRecipient {
......
public int count() {
return mCursor.getCount();
}
......
}
它的成員變量mCursor即為在前面
Step 22中創(chuàng)建的SQLiteCursor對(duì)象,于是,下面就會(huì)執(zhí)行SQLiteCursor類的getCount成員函數(shù)。
Step 24. SQLiteCursor.getCount
這個(gè)函數(shù)定義在frameworks/base/core/java/android/database/sqlite/SQLiteCursor.java文件中:
public class SQLiteCursor extends AbstractWindowedCursor {
......
@Override
public int getCount() {
if (mCount == NO_COUNT) {
fillWindow(0);
}
return mCount;
}
......
}
它里面的成員變量mCount的初始化為NO_COUNT,表示還沒(méi)有去執(zhí)行數(shù)據(jù)庫(kù)查詢操作,因此,還不知道它的值是多少,需要通過(guò)調(diào)用fillWindow函數(shù)來(lái)從數(shù)據(jù)據(jù)庫(kù)中查詢中,第三方應(yīng)用程序所請(qǐng)求的數(shù)據(jù)一共有多少行。
Step 25. QLiteCursor.fillWindow
這個(gè)函數(shù)定義在frameworks/base/core/java/android/database/sqlite/SQLiteCursor.java文件中:
public class SQLiteCursor extends AbstractWindowedCursor {
......
private void fillWindow (int startPos) {
......
mCount = mQuery.fillWindow(mWindow, mInitialRead, 0);
......
}
......
}
注意,這里的成員變量mWindow實(shí)際上是SQLiteCursor的父類
AbstractWindowedCursor的成員變量,是在Step 23中設(shè)置的,它的訪問(wèn)權(quán)限為protected,因此,SQLiteCursor類可以直接訪問(wèn)它。真正的數(shù)據(jù)庫(kù)查詢操作是由SQLiteCursor類的成員變量mQuery來(lái)執(zhí)行的,它的類型是SQLiteCursor,是前面的Step 22中創(chuàng)建的,它知道如何去把第三方應(yīng)用程序請(qǐng)求的數(shù)據(jù)從數(shù)據(jù)庫(kù)中提取出來(lái)。
Step 26. SQLiteCursor.fillWindow
這個(gè)函數(shù)定義在frameworks/base/core/java/android/database/sqlite/SQLiteQuery.java文件中:
public class SQLiteQuery extends SQLiteProgram {
......
/* package */ int fillWindow(CursorWindow window,
int maxRead, int lastPos) {
......
try {
......
try {
......
// if the start pos is not equal to 0, then most likely window is
// too small for the data set, loading by another thread
// is not safe in this situation. the native code will ignore maxRead
int numRows = native_fill_window(window, window.getStartPosition(), mOffsetIndex,
maxRead, lastPos);
......
return numRows;
} catch (IllegalStateException e){
......
} catch (SQLiteDatabaseCorruptException e) {
......
} finally {
......
}
} finally {
......
}
}
......
}
這里我們可以看到,真正的數(shù)據(jù)庫(kù)查詢操作是由本地方法native_fill_window來(lái)執(zhí)行的,它最終也是調(diào)用了sqlite的庫(kù)函數(shù)來(lái)執(zhí)行數(shù)據(jù)庫(kù)查詢的操作,這里我們就不跟進(jìn)去了,對(duì)sqlite有興趣的讀者可以自己研究一下。這個(gè)函數(shù)執(zhí)行完成之后,就會(huì)把從數(shù)據(jù)庫(kù)中查詢得到的數(shù)據(jù)的行數(shù)返回來(lái),這個(gè)行數(shù)最終返回到
Step 25中的SQLiteCursor.fillWindow函數(shù),設(shè)置在SQLiteCursor類的成員變量mCount中,于是,下次再調(diào)用它的getCount函數(shù)時(shí),就可以馬上返回了。
這一步執(zhí)行完成之后,就回到前面的Step 14中,最終就把從Content Provider中查詢得到的數(shù)據(jù)通過(guò)匿名共享內(nèi)存返回給第三方應(yīng)用程序了。
至此,Android應(yīng)用程序組件Content Provider在應(yīng)用程序之間共享數(shù)據(jù)的原理就分析完成了,總的來(lái)說(shuō),它就是通過(guò)Binder進(jìn)程間通信機(jī)制和匿名共享內(nèi)存來(lái)實(shí)現(xiàn)的了。
關(guān)于應(yīng)用程序間的數(shù)據(jù)共享還有另外的一個(gè)重要話題,就是數(shù)據(jù)更新通知機(jī)制了。因?yàn)閿?shù)據(jù)是在多個(gè)應(yīng)用程序中共享的,當(dāng)其中一個(gè)應(yīng)用程序改變了這些共享數(shù)據(jù)的時(shí)候,它有責(zé)任通知其它應(yīng)用程序,讓它們知道共享數(shù)據(jù)被修改了,這樣它們就可以作相應(yīng)的處理。在下一篇文章中,我們將分析Android應(yīng)用程序組件Content Provider的數(shù)據(jù)更新通知機(jī)制,敬請(qǐng)關(guān)注。
作者:Luoshengyang 發(fā)表于2011-12-5 1:00:28
原文鏈接
posted on 2012-04-17 21:32
mixer-a 閱讀(1456)
評(píng)論(0) 編輯 收藏