在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Android/ 第7章? 深入理解 ContentProvider
第2章? 深入理解 Java Binder 和 MessageQueue
第7章? 深入理解 ContentProvider
第5章? 深入理解 PowerManagerService
第3章? 深入理解 SystemServer
第8章? 深入理解ContentService 和 AccountManagerService
第1章?開(kāi)發(fā)環(huán)境部署
第4章? 深入理解 PackageManagerService
第6章?深入理解ActivityManagerService

第7章? 深入理解 ContentProvider

本章主要內(nèi)容:

·??深入分析ContentProvider的創(chuàng)建和啟動(dòng),以及SQLite相關(guān)的知識(shí)點(diǎn)

·??深入分析Cursor query和close函數(shù)的實(shí)現(xiàn)

·??深入分析ContentResolver openAssetFileDescriptor函數(shù)的實(shí)現(xiàn)

本章所涉及的源代碼文件名及位置:

·??ActivityManagerService.java

frameworks/base/services/java/com/android/server/am/ActivityManagerService.java

·??ContextImpl.java

frameworks/base/core/java/android/app/ContextImpl.java

·??ActivityThread.java

frameworks/base/core/java/android/app/ActivityThread.java

·??MediaStore.java

frameworks/base/core/java/android/provider/MediaStore.java

·??ContentResolver.java

frameworks/base/core/java/android/content/ContentResolver.java

·??ContentProvider.java

frameworks/base/core/java/android/content/ContentProvider.java

·??MediaProvider.java

package/providers/MediaProvider/src/java/com/android/MediaProvider/MediaProvider.java

·??SQLiteDatabase.java

frameworks/base/core/java/android/database/sqlite/SQLiteDatabase.java

·??SQLiteCompliteSql.java

frameworks/base/core/java/android/database/sqlite/SQLiteCompliteSql.java

·??android_database_SQLiteDatabase.cpp

frameworks/base/core/jni/android_database_SQLiteDatabase.cpp

·??android_database_SQLiteCompliteSql.cpp

frameworks/base/core/jni/android_database_SQLiteCompliteSql.cpp

·??sqlite3_android.cpp

external/sqlite3/android/sqlite3_android.cpp

·??SQLiteQueryBuilder.java

frameworks/base/core/java/android/database/sqlite/SQLiteQueryBuilder.java

·??SQLiteCursorDriver.java

frameworks/base/core/java/android/database/sqlite/SQLiteCursorDriver.java

·??SQLiteQuery.java

frameworks/base/core/java/android/database/sqlite/SQLiteQuery.java

·??SQLiteCursor.java

frameworks/base/core/java/android/database/sqlite/SQLiteCursor.java

·??SQLiteProgram.java

frameworks/base/core/java/android/database/sqlite/SQLiteProgram.java

·??CursorToBulkCursorAdaptor.java

frameworks/base/core/java/android/database/CursorToBulkCursorAdaptor.java

·??BulkCursorToCursorAdaptor.java

frameworks/base/core/java/android/database/BulkCursorToCursorAdaptor.java

·??CursorWindow.java

frameworks/base/core/java/android/database/CursorWindow.java

·??android_database_CursorWindow.cpp

frameworks/base/core/jni/android_database_CursorWindow.cpp

·??CursorWindow.cpp

frameworks/base/libs/binder/CursorWindow.cpp

·??android_database_SQLiteQuery.cpp

frameworks/base/core/jni/android_database_SQLiteQuery.cpp

·??CursorWrapper.java

frameworks/base/core/java/android/database/CursorWrapper.java

·??AbstractCursor.java

frameworks/base/core/java/android/database/AbstractCursor.java

·??BulkCursorNative.java

frameworks/base/core/java/android/database/BulkCursorNative.java

·??ParcelFileDescriptor.java

frameworks/base/core/java/android/os/ParcelFileDescriptor.java

·??MediaProvider.java

packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java

·??android_util_Binder.cpp

frameworks/base/core/jni/android_util_Binder.cpp

·??Parcel.cpp

frameworks/base/libs/binder/Parcel.cpp

·??binder.c

kernel/drivers/staging/android/binder.c

7.1 ?概述

本章重點(diǎn)分析ContentProvider、SQLite、Cursor query、close函數(shù)的實(shí)現(xiàn)及ContentResolver openAssetFileDescriptor函數(shù)。為了幫助讀者進(jìn)一步理解本章的知識(shí)點(diǎn),筆者特意挑選了四條分析路線(xiàn)。

·??第一條:以客戶(hù)端進(jìn)程通過(guò)MediaStore.Images.Media類(lèi)的靜態(tài)函數(shù)query來(lái)查詢(xún)MediaProvider中Image相關(guān)信息為入口點(diǎn),分析系統(tǒng)如何創(chuàng)建和啟動(dòng)MediaProvider。此分析路線(xiàn)著重關(guān)注客戶(hù)端進(jìn)程、ActivityManagerService及MediaProvider所在進(jìn)程間的交互。

·??第二條:沿襲第一條分析路徑,但是將關(guān)注焦點(diǎn)轉(zhuǎn)移到SQLiteDatabase如何創(chuàng)建數(shù)據(jù)庫(kù)的分析上。另外,本條路線(xiàn)還將對(duì)SQLite進(jìn)行相關(guān)介紹。

·??第三條:將重點(diǎn)研究Cursor query和close函數(shù)的實(shí)現(xiàn)細(xì)節(jié)。

·??第四條:將分析ContentResolver openAssetFileDescriptor函數(shù)的實(shí)現(xiàn)。

閑話(huà)少說(shuō),立即開(kāi)始本次分析之旅。

7.2? MediaProvider的啟動(dòng)及創(chuàng)建

第一、二、三條分析路線(xiàn)都將以下面這段示例為參考。

[-->MediaProvider客戶(hù)端示例]

void QueryImage(Context context){

? //①得到ContentResolver對(duì)象

?ContentResolver cr = context.getContentResover();

? Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

? //②查詢(xún)數(shù)據(jù)庫(kù)

? Cursorcursor = MediaStore.Images.Media.query(cr,uri,null);

?cursor.moveToFirst();//③移動(dòng)游標(biāo)到頭部

? ......//從游標(biāo)中取出數(shù)據(jù)集

??cursor.close();//④關(guān)閉游標(biāo)

}

先介紹一下這段示例的情況:客戶(hù)端(即運(yùn)行本示例的進(jìn)程)查詢(xún)(query)的目標(biāo)ContentProvider是MediaProvider,它運(yùn)行于進(jìn)程android.process.media中。假設(shè)目標(biāo)進(jìn)程此時(shí)還未啟動(dòng)。

本節(jié)的關(guān)注點(diǎn)集中在:

·??MediaProvider所在進(jìn)程是如何創(chuàng)建的?MediaProvider又是如何創(chuàng)建的?

·??客戶(hù)端通過(guò)什么和位于目標(biāo)進(jìn)程中的MediaProvider交互的?

先來(lái)看第一個(gè)關(guān)鍵函數(shù)getContentResolver。

7.2.1? Context的getContentResolver函數(shù)分析

根據(jù)第6章對(duì)Context的介紹,Context的getContentResolver最終會(huì)調(diào)用它所代理的ContextImpl對(duì)象的getContentResolver函數(shù),此處直接看ContextImpl的代碼。

[-->ContextImpl.java::getContentResolver]

public ContentResolver getContentResolver() {

?? returnmContentResolver;

}

該函數(shù)直接返回mContentResolver,此變量在ContextImpl初始化時(shí)創(chuàng)建,相關(guān)代碼如下:

[-->ContextImpl.java::init]

final void init(LoadedApk packageInfo, IBinder activityToken,

?????? ActivityThreadmainThread, Resources container,String basePackageName) {

? ?......

??mMainThread = mainThread;//mainThread指向AcitivityThread對(duì)象

? //mContentResolver的真實(shí)類(lèi)型是ApplicationContentResolver

??mContentResolver = new ApplicationContentResolver(this, mainThread);

?? ......

?}

由以上代碼可知,mContentResolver的真實(shí)類(lèi)型是ApplicationContentResolver,它是ContextImpl定義的內(nèi)部類(lèi)并繼承了ContentResolver。

getContentResolver函數(shù)比較簡(jiǎn)單,就分析到此。下面來(lái)看第二個(gè)關(guān)鍵點(diǎn)。

提示為了書(shū)寫(xiě)方便,將ContentProvider簡(jiǎn)稱(chēng)為CP,將ContentResolver簡(jiǎn)稱(chēng)為CR。

?

7.2.2? MediaStore.Image.Media的query函數(shù)分析

第二個(gè)關(guān)鍵點(diǎn)是在MediaProvider客戶(hù)端示例中所調(diào)用的MediaStore.Image.Media 的query函數(shù)。MediaStore是多媒體開(kāi)發(fā)中常用的類(lèi),其內(nèi)部定義了專(zhuān)門(mén)針對(duì)Image、Audio、Video等不同多媒體信息的內(nèi)部類(lèi)來(lái)幫助客戶(hù)端開(kāi)發(fā)人員更好底和MediaProvider交互。這些類(lèi)及相互之間的關(guān)系如圖7-1所示。

http://wiki.jikexueyuan.com/project/deep-android-v2/images/chapter7/image001.png" alt="image" />

圖7-1? MediaStore類(lèi)圖

由圖7-1可知,MediaStore定義了較多的內(nèi)部類(lèi),我們重點(diǎn)展示作為內(nèi)部類(lèi)之一的Image的情況,其中:

·??MediaColumns定義了所有與媒體相關(guān)的數(shù)據(jù)庫(kù)表都會(huì)用到的數(shù)據(jù)庫(kù)字段,而ImageColumns定義了單獨(dú)針對(duì)Image的數(shù)據(jù)庫(kù)字段。

·??Image類(lèi)定義了一個(gè)名為Media的內(nèi)部類(lèi)用于查詢(xún)和Image相關(guān)的信息,同時(shí)Image類(lèi)還定義了一個(gè)名為T(mén)humbnails的內(nèi)部類(lèi)用于查詢(xún)和Image相關(guān)的縮略圖的信息(在Android平臺(tái)上,縮略圖的來(lái)源有兩種,一種是Image,另一種是Video,故Image定義了名為T(mén)humbnails的內(nèi)部類(lèi),而Video也定義了一個(gè)名為T(mén)humbnails的內(nèi)部類(lèi))。

提示MediaStore類(lèi)較為復(fù)雜,主要原因是它定義了一些同名類(lèi)。讀者閱讀代碼時(shí)務(wù)須仔細(xì)。

下面來(lái)看Image.Media的query函數(shù),其代碼非常簡(jiǎn)單,如下所示:

[-->MediaStore.java::Image.Media.query]

public static final class Media implementsImageColumns {

?? public static final Cursor query(ContentResolvercr,Uri uri,

?????????????????????????????????????????? String[]projection) {

? ?//直接調(diào)用ContentResolver的query函數(shù)

?? returncr.query(uri, projection, null,

???????????????????? ?null,DEFAULT_SORT_ORDER);

?}

Image.Media的query函數(shù)直接調(diào)用ContentResolver的query函數(shù),雖然cr的真實(shí)類(lèi)型是ApplicationContentResolver,但是此函數(shù)卻由其基類(lèi)ContentResolver實(shí)現(xiàn)。

提示追求運(yùn)行效率的程序員也許會(huì)對(duì)上邊這段代碼的實(shí)現(xiàn)略有微詞,因?yàn)镮mage.Media的query函數(shù)基本上沒(méi)做任何有意義的工作。假如客戶(hù)端直接調(diào)用cr.query函數(shù),則此處的query就增加了一次函數(shù)調(diào)用和返回的開(kāi)銷(xiāo)(即Image.Media query調(diào)用和返回時(shí)參數(shù)的入棧/出棧)。但是,通過(guò)Image.Media的封裝將使程序更清晰和易讀(與直接使用ContentResolver的query相比,代碼閱讀者一看Image.Media就知道其query函數(shù)應(yīng)該和Image有關(guān),否則需要通過(guò)解析uri參數(shù)才能確定查詢(xún)的信息是什么)。代碼清晰易讀和運(yùn)行效率高,往往是軟件開(kāi)發(fā)中的熊掌和魚(yú),它們之間的對(duì)立性,將在本章中體現(xiàn)得淋漓盡致。筆者建議讀者在實(shí)際開(kāi)發(fā)中結(jié)合具體情況決定取舍,萬(wàn)不可鉆牛角尖。

1.? ContentResolver的query函數(shù)分析

[-->ContentResolver.java::query]

public final Cursor query(Uri uri, String[]projection,

???????????String selection, String[] selectionArgs, String sortOrder) {

?? //調(diào)用acquireProvider函數(shù),參數(shù)為uri,函數(shù)也由ContentResolver實(shí)現(xiàn)

??IContentProvider provider = acquireProvider(uri);

? //注意:下面將和ContentProvider交互, 相關(guān)知識(shí)留待7.4節(jié)再分析

?? ......

?}

ContentResolver的query將調(diào)用acquireProvider,該函數(shù)定義在ContentResolver類(lèi)中,代碼如下:

[-->ContentResolver.java::query]

public final IContentProvider acquireProvider(Uriuri) {

??if(!SCHEME_CONTENT.equals(uri.getScheme()))?return null;

? Stringauth = uri.getAuthority();

? if (auth!= null) {

???? //acquireProvider是一個(gè)抽象函數(shù),由ContentResolver的子類(lèi)實(shí)現(xiàn)。在本例中,該函數(shù)

???? //將由ApplicationContentResolver實(shí)現(xiàn)。uri.getAuthority將返回代表目標(biāo)

??? ?//ContentProvider的名字

????? returnacquireProvider(mContext, uri.getAuthority());

? }

? returnnull;

?}

如上所述,acquireProvider由ContentResolver的子類(lèi)實(shí)現(xiàn),在本例中該函數(shù)由ApplicationContentResolver定義,代碼如下:

[-->ContextImpl.java::acquireProvider]

protected IContentProvider acquireProvider(Contextcontext, String name) {

???//mMainThread指向代表應(yīng)用進(jìn)程主線(xiàn)程的ActivityThread對(duì)象,每個(gè)應(yīng)用進(jìn)程只有一個(gè)

???//ActivityThread對(duì)象

?? ?return mMainThread.acquireProvider(context,name);

?}

如以上代碼所示,最終ActivityThread的acquireProvider函數(shù)將被調(diào)用,希望它不要再被層層轉(zhuǎn)包了。

2.? AcitvityThread的acquireProvider函數(shù)分析

ActivityThread的 acquireProvider函數(shù)的代碼如下:

[-->ActivityThread.java::acquireProvider]

public final IContentProvideracquireProvider(Context c, String name) {

? //①調(diào)用getProvider函數(shù),它很重要。見(jiàn)下文分析

??IContentProvider provider = getProvider(c,name);

? ......

? IBinderjBinder = provider.asBinder();

??synchronized(mProviderMap) {

???? //客戶(hù)端進(jìn)程將本進(jìn)程使用的ContentProvider信息保存到mProviderRefCountMap中,

???? //其主要功能與引用計(jì)數(shù)和資源釋放有關(guān),讀者暫可不理會(huì)它

?????ProviderRefCount prc = mProviderRefCountMap.get(jBinder);

????? if(prc== null)

???????? ?mProviderRefCountMap.put(jBinder, newProviderRefCount(1));

?????else?? prc.count++;

?? }

?returnprovider;

}

在acquireProvider內(nèi)部調(diào)用getProvider得到一個(gè)IContentProvider類(lèi)型的對(duì)象,該函數(shù)非常重要,其代碼為:

[-->ActivityThread.java::getProvider]

private IContentProvider getProvider(Contextcontext, String name) {

? /*

?? 查詢(xún)?cè)搼?yīng)用進(jìn)程是否已經(jīng)保存了用于和遠(yuǎn)端ContentProvider通信的對(duì)象existing。

?? 此處,我們知道existing的類(lèi)型是IContentProvider,不過(guò)IContentProvider是一

?? 個(gè)interface,那么existing的真實(shí)類(lèi)型是什么呢?稍后再揭示

? */

?IContentProvider existing = getExistingProvider(context, name);

? if(existing != null) return existing;//如果existing存在,則直接返回

?

?IActivityManager.ContentProviderHolder holder = null;

? try {

????? //如果existing不存在,則需要向AMS查詢(xún),返回值的類(lèi)型為ContentProviderHolder

????? holder= ActivityManagerNative.getDefault().getContentProvider(

???????????????getApplicationThread(), name);

? }......

?? //注意:記住下面這個(gè)函數(shù)調(diào)用,此時(shí)是在客戶(hù)端進(jìn)程中

?IContentProvider prov = installProvider(context, holder.provider,

???????????????holder.info, true);

?? ......

? returnprov;

?}

以上代碼中讓人比較頭疼的是其中新出現(xiàn)的幾種數(shù)據(jù)類(lèi)型,如IContentProvider、ContentProviderHolder。先來(lái)分析AMS的getContentProvider。

3.? AMS的getContentProvider函數(shù)分析

getContentProvider的功能主要由getContentProviderImpl函數(shù)實(shí)現(xiàn),故此處可直接對(duì)它進(jìn)行分析。

(1)?getContentProviderImpl啟動(dòng)目標(biāo)進(jìn)程

getContentProviderImpl函數(shù)較長(zhǎng),可分段來(lái)看,先來(lái)分析下面一段代碼。

[-->ActivityManagerService.java::getContentProviderImpl]

?privatefinal ContentProviderHolder getContentProviderImpl(

?????? ???????????????????IApplicationThread caller, String name) {

?ContentProviderRecord cpr;

?ProviderInfo cpi = null;

?

?synchronized(this) {

????ProcessRecord r = null;

???? if(caller != null) {

????????? r= getRecordForAppLocked(caller);

??????????if (r == null)......//如果查詢(xún)不到調(diào)用者信息,則拋出SecurityException

? ????}// if (caller != null)判斷結(jié)束

??? //name參數(shù)為調(diào)用進(jìn)程指定的代表目標(biāo)ContentProvider的authority

??? cpr =mProvidersByName.get(name);

??? //如果cpr不為空,表明該ContentProvider已經(jīng)在AMS中注冊(cè)

??? booleanproviderRunning = cpr != null;

??? if(providerRunning){

???????......//如果該ContentProvider已經(jīng)存在,則進(jìn)行對(duì)應(yīng)處理, 相關(guān)內(nèi)容可自行閱讀

??? }

?? //如果目標(biāo)ContentProvider對(duì)應(yīng)進(jìn)程還未啟動(dòng)

?? if(!providerRunning) {

????? ?try {

???????????//查詢(xún)PKMS,得到指定的ProviderInfo信息

???????????cpi = AppGlobals.getPackageManager().resolveContentProvider(

???????????????????name,STOCK_PM_FLAGS |

???????????????????PackageManager.GET_URI_PERMISSION_PATTERNS);

?????? ?}......

??????? String msg;

????? //權(quán)限檢查,此處不作討論

????? if((msg=checkContentProviderPermissionLocked(cpi, r)) != null)

?????????throw new SecurityException(msg);

???? /*

????? 如果system_server還沒(méi)啟動(dòng)完畢,并且該ContentProvider不運(yùn)行在system_server

????? 中,則此時(shí)不允許啟動(dòng)ContentProvider。讀者還記得哪個(gè)ContentProvider運(yùn)行在

?????system_server進(jìn)程中嗎?答案是SettingsProvider

? ??*/

???? .......

?

????ComponentName comp = new ComponentName(cpi.packageName, cpi.name);

???? cpr =mProvidersByClass.get(comp);

???? finalboolean firstClass = cpr == null;

???? //初次啟動(dòng)MediaProvider對(duì)應(yīng)進(jìn)程時(shí),firstClass一定為true

??? ?if (firstClass) {

????? ???try {

?????????????//查詢(xún)PKMS,得到MediaProvider所在的Application信息

???????? ?????ApplicationInfoai =

??????????????????????AppGlobals.getPackageManager().getApplicationInfo(

??????????????????????????cpi.applicationInfo.packageName, STOCK_PM_FLAGS);

?????????? ??if (ai == null) return null;

????????????//在AMS內(nèi)部通過(guò)ContentProviderRecord來(lái)保存ContentProvider的信息,類(lèi)似

????????????//ActivityRecord,BroadcastRecord等

????????? ???cpr = new ContentProviderRecord(cpi, ai,comp);

?????? ??}......

??? }// if(firstClass)判斷結(jié)束

以上代碼的邏輯比較簡(jiǎn)單,主要是為目標(biāo)ContentProvider(即MediaProvider)創(chuàng)建一個(gè)ContentProviderRecord對(duì)象。結(jié)合第6章的知識(shí),AMS為四大組件都設(shè)計(jì)了對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu),如ActivityRecord、BroadcastRecord等。

接著看getContentProviderImpl,其下一步的工作就是啟動(dòng)目標(biāo)進(jìn)程:

[-->ActivityManagerService.java::getContentProviderImpl]

?? /*

?? canRunHere函數(shù)用于判斷目標(biāo)CP能否運(yùn)行在r所對(duì)應(yīng)的進(jìn)程(即調(diào)用者所在進(jìn)程)

?? 該函數(shù)內(nèi)部做如下判斷:

?? (info.multiprocess|| info.processName.equals(app.processName))

????&& (uid == Process.SYSTEM_UID || uid == app.info.uid)

?? ?就本例而言,MediaProvider不能運(yùn)行在客戶(hù)端進(jìn)程中

?? */

??? if (r !=null && cpr.canRunHere(r)) ?returncpr;

?

??? finalint N = mLaunchingProviders.size();

??? ......//查找目標(biāo)ContentProvider對(duì)應(yīng)的進(jìn)程是否正處于啟動(dòng)狀態(tài)

??? //如果i大于等于N,表明目標(biāo)進(jìn)程的信息不在mLaunchingProviders中

??? if (i>= N) {

?????? finallong origId = Binder.clearCallingIdentity();

?????? ......

?????? //調(diào)用startProcessLocked函數(shù)創(chuàng)建目標(biāo)進(jìn)程

??????ProcessRecord proc = startProcessLocked(cpi.processName,

???????????????????????? cpr.appInfo, false, 0,"content provider",

???????????????????????? newComponentName(cpi.applicationInfo.packageName,

????????????????????????? cpi.name), false);

?????? if(proc == null)return null;

??????cpr.launchingApp = proc;

?????? //將該進(jìn)程信息保存到mLaunchingProviders中

??????mLaunchingProviders.add(cpr);

??? }

?

???? if(firstClass) mProvidersByClass.put(comp, cpr);

????mProvidersByName.put(name, cpr);

???? /*

?????? 下面這個(gè)函數(shù)將為客戶(hù)端進(jìn)程和目標(biāo)CP進(jìn)程建立緊密的關(guān)系,即當(dāng)目標(biāo)CP進(jìn)程死亡后,

??????? AMS將根據(jù)該函數(shù)建立的關(guān)系找到客戶(hù)端進(jìn)程并殺死(kill)它們。在7.2.3節(jié)

?????? ?有對(duì)這個(gè)函數(shù)的相關(guān)解釋

???? */

???? incProviderCount(r, cpr);

???? if(cpr.launchingApp == null) return null;

???? try {

??????????cpr.wait();//等待目前進(jìn)程的啟動(dòng)

????? } ......

?? }// synchronized(this)結(jié)束

? returncpr;

}

通過(guò)對(duì)以上代碼的分析發(fā)現(xiàn),getContentProviderImpl將等待一個(gè)事件,想必讀者也能明白,此處一定是在等待目標(biāo)進(jìn)程啟動(dòng)并創(chuàng)建好MediaProvider。目標(biāo)進(jìn)程的這部分工作用專(zhuān)業(yè)詞語(yǔ)來(lái)表達(dá)就是發(fā)布目標(biāo)ContentProvider(即本例的MediaProvider)。

(2)?MediaProvider的創(chuàng)建

根據(jù)第6章的介紹,目標(biāo)進(jìn)程啟動(dòng)后要做的第一件大事就是調(diào)用AMS的attachApplication函數(shù),該函數(shù)的主要功能由attachApplicationLocked完成。我們回顧一下相關(guān)代碼。

[-->ActivityManagerService.java::attachApplicationLocked]

private final booleanattachApplicationLocked(IApplicationThread thread,

???????????int pid) {

?? ......

?? //通過(guò)PKMS查詢(xún)運(yùn)行在該進(jìn)程中的CP信息,并保存到mProvidersByClass中

?? Listproviders = normalMode ?

???????????????generateApplicationProvidersLocked(app) : null;

? //調(diào)用目標(biāo)應(yīng)用進(jìn)程的bindApplication函數(shù),此處將providers信息傳遞給目標(biāo)進(jìn)程

?? thread.bindApplication(processName,appInfo, providers,

?????????????????????????????app.instrumentationClass, profileFile,

???????????????????? ?????????......);

?? ......

}

再來(lái)看目標(biāo)進(jìn)程bindApplication的實(shí)現(xiàn),其內(nèi)部最終會(huì)通過(guò)handleBindApplication函數(shù)處理,我們回顧一下相關(guān)代碼。

[-->ActivtyThread.java::handleBindApplication]

private void handleBindApplication(AppBindDatadata) {

?? ......

?? if(!data.restrictedBackupMode){

???????List<ProviderInfo> providers = data.providers;

??????? if(providers != null) {

???????????//調(diào)用installContentProviders安裝

???????????installContentProviders(app, providers);

???????????......

?????? }

? ?}

?? ......

}

AMS傳遞過(guò)來(lái)的ProviderInfo列表將由目標(biāo)進(jìn)程的installContentProviders處理,其相關(guān)代碼如下:

[-->ActivtyThread.java::installContentProviders]

private void installContentProviders(Contextcontext,

????? ????????????????????????????????????????List<ProviderInfo>providers) {

?

? finalArrayList<IActivityManager.ContentProviderHolder> results =

????????? ??????newArrayList<IActivityManager.ContentProviderHolder>();

?Iterator<ProviderInfo> i = providers.iterator();

? while(i.hasNext()) {

???? //①也調(diào)用installProvider,注意該函數(shù)傳遞的第二個(gè)參數(shù)為null

????IContentProvider cp = installProvider(context, null, cpi, false);

???? if (cp!= null) {

?????????IActivityManager.ContentProviderHolder cph =

??????????? ??????????????newIActivityManager.ContentProviderHolder(cpi);

?????????cph.provider = cp;

??????????results.add(cph);//將信息添加到results數(shù)組中

?????????? ......//創(chuàng)建引用計(jì)數(shù)

???????? }

??? }//while循環(huán)結(jié)束

?

??? try {? //②調(diào)用AMS的publishContentProviders發(fā)布ContentProviders

???????????ActivityManagerNative.getDefault().publishContentProviders(

???????????????getApplicationThread(), results);

??????? } ......

?}

以上代碼列出了兩個(gè)關(guān)鍵點(diǎn),分別是:

·??調(diào)用installProvider得到一個(gè)IContentProvider類(lèi)型的對(duì)象。

·??調(diào)用AMS的publishContentProviders發(fā)布本進(jìn)程所運(yùn)行的ContentProvider。該函數(shù)留到后面再作分析

在繼續(xù)分析之前,筆者要特別強(qiáng)調(diào)installProvider,該函數(shù)既在客戶(hù)端進(jìn)程中被調(diào)用(還記得7.2.2節(jié)ActivityThread的acquireProvider函數(shù)中那句注釋嗎?),又在目標(biāo)進(jìn)程(即此處MediaProvider所在進(jìn)程)中被調(diào)用。與客戶(hù)端進(jìn)程的調(diào)用相比,只在一處有明顯的不同:

·??客戶(hù)端進(jìn)程調(diào)用installProvider函數(shù)時(shí),該函數(shù)的第二個(gè)參數(shù)不為null。

·??目標(biāo)進(jìn)程調(diào)用installProvider函數(shù)時(shí),該函數(shù)的第二個(gè)參數(shù)硬編碼為null。

我們?cè)?jīng)在6.2.3分析過(guò)installProvider函數(shù),結(jié)合那里的介紹可知:installProvider是一個(gè)通用函數(shù),不論客戶(hù)端使用遠(yuǎn)端的CP還是目標(biāo)進(jìn)程安裝運(yùn)行在其上的CP上,最終都會(huì)調(diào)用它,只不過(guò)參數(shù)不同罷了。

來(lái)看installProvider函數(shù),其代碼如下:

[-->ActivityThread.java::installProvider]

private IContentProvider installProvider(Contextcontext,

???????????IContentProvider provider, ProviderInfo info, boolean noisy) {

??ContentProvider localProvider = null;

?? if(provider == null) {//針對(duì)目標(biāo)進(jìn)程的情況

???????Context c = null;

????????ApplicationInfo ai = info.applicationInfo;

???????? if(context.getPackageName().equals(ai.packageName)) {

???????????????c = context;

???????????}......//這部分代碼已經(jīng)在6.2.3節(jié)分析過(guò)了,其目的就是為了得到正確的

??????????//Context用于加載Java字節(jié)碼

???????? try{

????????????final java.lang.ClassLoader cl = c.getClassLoader();

????????????//通過(guò)Java反射機(jī)制創(chuàng)建MediaProvider實(shí)例

????????????localProvider = (ContentProvider)cl.

?????????????????????????????????????loadClass(info.name).newInstance();

????????????//注意下面這句代碼

????????????provider = localProvider.getIContentProvider();

??????????? }

???? } elseif (localLOGV) {

???????????Slog.v(TAG, "Installing external provider " + info.authority +": "

???????????????????+ info.name);

??? }// if(provider == null)判斷結(jié)束

??? /*

???? 由以上代碼可知,對(duì)于provider不為null 的情況(即客戶(hù)端調(diào)用的情況),該函數(shù)沒(méi)有

???? 什么特殊的處理

??? */

??? ......

???? /*

????? 引用計(jì)數(shù)及設(shè)置DeathReceipient等相關(guān)操作。在6.2.3節(jié)的第2個(gè)小標(biāo)題中

????? 曾說(shuō)過(guò),目標(biāo)進(jìn)程為自己進(jìn)程中的CP實(shí)例設(shè)置DeathReceipient沒(méi)有作用,因?yàn)槎咴谕粋€(gè)

????? 進(jìn)程中,自己怎么能接收自己的訃告消息呢?不過(guò),如果客戶(hù)端進(jìn)程為目標(biāo)進(jìn)程的CP設(shè)置

???? DeathReceipient又有作用嗎?仔細(xì)思考這個(gè)問(wèn)題

??? */

??? returnprovider;//最終返回的對(duì)象是IContentProvider類(lèi)型,它到底是什么呢?

?}

由以代碼可知,installProvider最終返回的是一個(gè)IContentProvider類(lèi)型的對(duì)象。對(duì)于目標(biāo)進(jìn)程而言,該對(duì)象是通過(guò)調(diào)用CP的實(shí)例對(duì)象的(本例就是MediaProvider) getIContentProvider函數(shù)得來(lái)的。而對(duì)于客戶(hù)端進(jìn)程而言,該對(duì)象是由installProvider第二個(gè)參數(shù)傳遞進(jìn)來(lái)的,那么,這個(gè)IContentProvider到底是什么?

(3)?IContentProvider的真面目

要說(shuō)清楚IContentProvider,就先來(lái)看ContentProvider家族的類(lèi)圖,如圖7-2所示。

http://wiki.jikexueyuan.com/project/deep-android-v2/images/chapter7/image002.png" alt="image" />

圖7-2? ContentProvider類(lèi)圖

圖7-2揭示了IContentProvider的真面目,具體介紹如下:

·??每個(gè)ContentProvider實(shí)例中都有一個(gè)mTransport成員,其類(lèi)型為T(mén)ransport。

·??Transport類(lèi)從ContentProviderNative派生。由圖7-2可知,ContentProviderNative從Binder類(lèi)派生,并實(shí)現(xiàn)了IContentProvider接口。結(jié)合前面的代碼,IContentProvider將是客戶(hù)端進(jìn)程和目標(biāo)進(jìn)程交互的接口,即目標(biāo)進(jìn)程使用IContentProvider的Bn端Transport,而客戶(hù)端使用IContentProvider的Bp端,其類(lèi)型是ContentProviderProxy(定義在ContentProviderNative.java中)。

客戶(hù)端如何通過(guò)IContentProvider query函數(shù)和目標(biāo)CP進(jìn)程交互的呢?其流程如下:

·??CP客戶(hù)端得到IContentProvider的Bp端(實(shí)際類(lèi)型是ContentProviderProxy),并調(diào)用其query函數(shù),在該函數(shù)內(nèi)部將參數(shù)信息打包,傳遞給Transport(它是IContentProvider的Bn端)。

·??Transport的onTransact函數(shù)將調(diào)用Transport的query函數(shù),而Transport的query函數(shù)又將調(diào)用ContentProvider子類(lèi)定義的query函數(shù)(即MediaProvider的query函數(shù))。

關(guān)于目標(biāo)進(jìn)程這一系列的調(diào)用函數(shù),不妨先看看Transport的query函數(shù),其代碼為:

[-->ContentProvider.java::Transport.query]

public Cursor query(Uri uri, String[] projection,

???????????????String selection, String[] selectionArgs, String sortOrder) {

??enforceReadPermission(uri);

??//Transport為ContentProvider的內(nèi)部類(lèi),此處將調(diào)用ContentProvider的query函數(shù)

?? //本例中,該query函數(shù)由MediaProvider實(shí)現(xiàn),故最終會(huì)調(diào)用MediaProvider的query

?? returnContentProvider.this.query(uri, projection, selection,

???????????????????selectionArgs, sortOrder);

?}

務(wù)必弄清楚,此處只有一個(gè)目標(biāo)ContentProvider的實(shí)例,即只有一個(gè)MediaProvider對(duì)象。Transport的 query函數(shù)內(nèi)部雖然調(diào)用的是基類(lèi)ContentProvider的query函數(shù),但是根據(jù)面向?qū)ο蟮亩鄳B(tài)原理,該函數(shù)最終由其子類(lèi)(本例中是MediaProvider)來(lái)實(shí)現(xiàn)。

認(rèn)識(shí)了IContentProvider,即知道了客戶(hù)端進(jìn)程和目標(biāo)進(jìn)程的交互接口。

繼續(xù)我們的分析。此時(shí)目標(biāo)進(jìn)程需要將MediaProvider的信息通過(guò)AMS發(fā)布出去。

(4) ?AMS pulishContentProviders分析

要把目標(biāo)進(jìn)程的CP信息發(fā)布出去,需借助AMS 的pulishContentProviders函數(shù),其代碼如下:

[-->ActivityManagerService.java::publishContentProviders]

public final voidpublishContentProviders(IApplicationThread caller,

?????????List<ContentProviderHolder> providers) {

?? ......

??synchronized(this) {

???? finalProcessRecord r = getRecordForAppLocked(caller);

???? finallong origId = Binder.clearCallingIdentity();

?

??? ?final int N = providers.size();

??? ?for (int i=0; i<N; i++) {

???????ContentProviderHolder src = providers.get(i);

???????ContentProviderRecord dst = r.pubProviders.get(src.info.name);

?????? if(dst != null) {

????????? ?......//將相關(guān)信息分別保存到mProviderByClass和mProvidersByName中

???????? ??int NL = mLaunchingProviders.size();

?????????...... //目標(biāo)進(jìn)程已經(jīng)啟動(dòng),將其從mLaunchingProviders移除

?????????synchronized (dst) {

????????????dst.provider = src.provider;//將信息保存在dst中

????????????dst.proc = r;

???????????//觸發(fā)還等在(wait)getContentProvider中的那個(gè)客戶(hù)端進(jìn)程

????????????dst.notifyAll();

? ????????}

????? ?updateOomAdjLocked(r);//調(diào)節(jié)目標(biāo)進(jìn)程的oom_adj等相關(guān)參數(shù)

???? }// if(dst != null)判斷結(jié)束

?? ......

?? }

}

至此,客戶(hù)端進(jìn)程將從getContentProvider中返回,并調(diào)用installProvider函數(shù)。根據(jù)前面的分析,客戶(hù)端進(jìn)程調(diào)用installProvider時(shí),其第二個(gè)參數(shù)不為null,即客戶(hù)端進(jìn)程已經(jīng)從AMS中得到了能直接和目標(biāo)進(jìn)程交互的IContentProvider Bp端對(duì)象。此后,客戶(hù)端就可直接使用該對(duì)象向目標(biāo)進(jìn)程發(fā)起請(qǐng)求。

7.2.3? MediaProvider的啟動(dòng)及創(chuàng)建總結(jié)

回顧一下整個(gè)MediaProvider的啟動(dòng)和創(chuàng)建過(guò)程,如圖7-3所示。

http://wiki.jikexueyuan.com/project/deep-android-v2/images/chapter7/image003.png" alt="image" />

圖7-3? MediaProvider的啟動(dòng)和創(chuàng)建流程

整個(gè)流程相對(duì)比較簡(jiǎn)單。讀者在分析時(shí)只要注意installProvider這個(gè)函數(shù)在目標(biāo)進(jìn)程和客戶(hù)端進(jìn)程中被調(diào)用時(shí)的區(qū)別即可。這里再次強(qiáng)調(diào):

·??目標(biāo)進(jìn)程調(diào)用installProvider時(shí),傳遞的第二個(gè)參數(shù)為null,使內(nèi)部通過(guò)Java反射機(jī)制真正創(chuàng)建目標(biāo)CP實(shí)例。

·??客戶(hù)端調(diào)用installProvider時(shí),其第二個(gè)參數(shù)已經(jīng)通過(guò)查詢(xún)AMS得到。該函數(shù)真正的工作只不過(guò)是引用計(jì)數(shù)控制和設(shè)置訃告接收對(duì)象罷了。

至此,客戶(hù)端進(jìn)程和目標(biāo)進(jìn)程通信的通道IContentProvider已經(jīng)登場(chǎng)。除此之外,客戶(hù)端進(jìn)程和目標(biāo)CP還建立了非常緊密的關(guān)系,這種關(guān)系造成的后果就是一旦目標(biāo)CP進(jìn)程死亡,AMS會(huì)殺死與之有關(guān)的客戶(hù)端進(jìn)程。不妨回顧一下與之相關(guān)的知識(shí)點(diǎn):

·??該關(guān)系的建立是在AMS getContentProviderImpl函數(shù)中調(diào)用incProviderCount完成的,關(guān)系的確立以ContentProviderRecorder保存客戶(hù)端進(jìn)程的ProcessRecord信息為標(biāo)識(shí)。

·??一旦CP進(jìn)程死亡,AMS能根據(jù)該ContentProviderRecorder中保存的客戶(hù)端信息找到使用該CP的所有客戶(hù)端進(jìn)程,然后再殺死它們。

客戶(hù)端能否撤銷(xiāo)這種緊密關(guān)系呢?答案是肯定的,但這和Cursor是否關(guān)閉有關(guān)。這里先簡(jiǎn)單描述一下流程:

·??當(dāng)Cursor關(guān)閉時(shí),ContextImpl的releaseProvider會(huì)被調(diào)用。根據(jù)前面的介紹,它最終會(huì)調(diào)用ActivityThread的releaseProvider函數(shù)。

·??ActivityThread的releaseProvider函數(shù)會(huì)導(dǎo)致completeRemoveProvider被調(diào)用,在其內(nèi)部根據(jù)該CP的引用計(jì)數(shù)判斷是否需要調(diào)用AMS的removeContentProvider。

·??通過(guò)AMS的removeContentProvider將刪除對(duì)應(yīng)ContentProviderRecord中此客戶(hù)端進(jìn)程的信息,這樣一來(lái),客戶(hù)端進(jìn)程和目標(biāo)CP進(jìn)程的緊密關(guān)系就蕩然無(wú)存了。

至此,本章第一條分析路線(xiàn)就介紹完畢。

提示讀者可能覺(jué)得,這條路線(xiàn)是對(duì)第6章的補(bǔ)充和延續(xù)。不過(guò),雖然目標(biāo)進(jìn)程由AMS創(chuàng)建和啟動(dòng),而且ContentProvider的發(fā)布也需和AMS交互,但是對(duì)于ContentProvider來(lái)說(shuō),我們更關(guān)注客戶(hù)端和目標(biāo)進(jìn)程中ContentProvider實(shí)例間的交互。事實(shí)上,客戶(hù)端得到IContentProvider Bp端對(duì)象后,即可直接與目標(biāo)進(jìn)程的CP實(shí)例交互,也就無(wú)需借助AMS了,所以筆者將這條路線(xiàn)放到了本章進(jìn)行分析。

7.3? SQLite創(chuàng)建數(shù)據(jù)庫(kù)分析

作為Android多媒體系統(tǒng)中媒體信息的倉(cāng)庫(kù),MediaProvider使用了SQLite數(shù)據(jù)庫(kù)來(lái)管理系統(tǒng)中多媒體相關(guān)的數(shù)據(jù)信息。作為第二條分析路線(xiàn),本節(jié)的目標(biāo)是分析MediaProvider如何利用SQLite創(chuàng)建數(shù)據(jù)庫(kù),同時(shí)還將介紹和SQLite相關(guān)的一些知識(shí)點(diǎn)。

先來(lái)看大名鼎鼎的SQLite及Java層的SQLiteDatabase家族。

7.3.1? SQLite及SQLiteDatabase家族

1.? SQLite輕裝上陣

SQLite是一個(gè)輕量級(jí)的數(shù)據(jù)庫(kù),它和筆者之前接觸的SQLServer或Oracle DB比起來(lái),猶如螞蟻和大象的區(qū)別。它“輕”到什么程度呢?筆者總結(jié)了SQLite具有的兩個(gè)特點(diǎn):

·??從代碼上看,SQLite所有的功能都實(shí)現(xiàn)在Sqlite3.c中,而頭文件Sqlite3.h定義了它所支持的API。其中,Sqlite3.c文件包含12萬(wàn)行左右的代碼,相當(dāng)于一個(gè)中等偏小規(guī)模的程序。

·??從使用者角度的來(lái)說(shuō),SQLite編譯完成后將生成一個(gè)libsqlite.so,大小僅為300多KB。

SQLite確實(shí)夠輕,但這個(gè)“輕”還不是本節(jié)標(biāo)題“輕裝上陣”一詞中的“輕”。為什么?此處先向讀者們介紹一個(gè)直接使用SQLite API開(kāi)發(fā)的Android native程序示例,該示例最終編譯成的二進(jìn)制可執(zhí)行程序名為sqlitetest。

注意本書(shū)后文所說(shuō)的SQLite API特指libsqlite.so提供的Native層的API。

使用SQLite API開(kāi)發(fā)的Android native程序示例的代碼如下:

[-->SqliteTest.cpp::libsqlite示例]

#include <unistd.h>

#include <sqlite3.h> //包含sqlite API頭文件,這里的3是SQLite的版本號(hào)

#include <stdlib.h>

?

#define LOG_TAG "SQLITE_TEST"? //定義該進(jìn)程logcat輸出的標(biāo)簽

#include <utils/Log.h>

#ifndef NULL

?? #defineNULL (0)

#endif

//聲明數(shù)據(jù)庫(kù)文件的路徑

#define DB_PATH"/mnt/sdcard/sqlite3test.db"

/*

?? 聲明一個(gè)全局的SQLite句柄,開(kāi)發(fā)者無(wú)需了解該數(shù)據(jù)結(jié)構(gòu)的具體內(nèi)容,只要知道它代表了使用者

?? 和數(shù)據(jù)庫(kù)的一種連接關(guān)系即可。以后凡是針對(duì)特定數(shù)據(jù)庫(kù)的操作,都需要傳入對(duì)應(yīng)的SQLite句柄

*/

static sqlite3* g_pDBHandle = NULL;

?

/*

?? 定義一個(gè)宏,用于檢測(cè)SQLite API調(diào)用的返回值,如果value不等于expectValue,則打印警告,

?? 并退出程序。注意,進(jìn)程退出后,系統(tǒng)會(huì)自動(dòng)回收分配的內(nèi)存資源。對(duì)如此簡(jiǎn)單的例子來(lái)說(shuō),讀者大可

??? 不必苛責(zé)。其中,sqlite3_errmsg函數(shù)用于打印錯(cuò)誤信息

*/

#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);//先刪除舊的數(shù)據(jù)庫(kù)文件

??LOGD("Create new DB file");

?? /*

??? 調(diào)用sqlite3_open創(chuàng)建一個(gè)數(shù)據(jù)庫(kù),并將和該數(shù)據(jù)庫(kù)的連接環(huán)境保存在全局的SQLite句柄

???? g_pDBHandle中,以后操作g_pDBHandle就是操作DB_PATH對(duì)應(yīng)的數(shù)據(jù)庫(kù)

??? */

?? int ret =sqlite3_open(DB_PATH,&g_pDBHandle);

??CHECK_DB_ERR(ret,SQLITE_OK);

??

??LOGD("Create Table personal_info:");

?? /*

??? 定義宏TABLE_PERSONAL_INFO,用于描述為本例數(shù)據(jù)庫(kù)建立一個(gè)表所用的SQL語(yǔ)句,

??? 不熟悉SQL語(yǔ)句的讀者可先學(xué)習(xí)相關(guān)知識(shí)。從該宏的定義可知,將建立一個(gè)名為

???personal_info的表,該表有4列,第一列是主鍵,類(lèi)型是整型(SQLite中為INTEGER),

??? 每加入一行數(shù)據(jù)該值會(huì)自動(dòng)遞增;第二列名為"name",數(shù)據(jù)類(lèi)型是字符串(SQLite中為T(mén)EXT);

???? 第三列名為“age”,數(shù)據(jù)類(lèi)型是整型;第四列名為“sex”,數(shù)據(jù)類(lèi)型是字符串

?? */

?? #defineTABLE_PERSONAL_INFO? \

?? ???????"CREATETABLE personal_info" \

?? ????????"(ID INTEGER primary keyautoincrement," \

?????????? "nameTEXT," \

?? ????????"age INTEGER,"\

?? ????????"sex TEXT"\

?? ????????")"

?? //打印TABLE_PERSONAL_INFO所對(duì)應(yīng)的SQL語(yǔ)句

??LOGD("\t%s\n",TABLE_PERSONAL_INFO);

? //調(diào)用sqlite3_exec執(zhí)行前面那條SQL語(yǔ)句

?? ret =sqlite3_exec(g_pDBHandle,TABLE_PERSONAL_INFO,NULL,NULL,NULL);

??CHECK_DB_ERR(ret,SQLITE_OK);

??

?? /*

??? 定義插入一行數(shù)據(jù)所使用的SQL語(yǔ)句,注意最后一行中的問(wèn)號(hào),它表示需要在插入數(shù)據(jù)

?? 前和具體的值綁定。插入數(shù)據(jù)庫(kù)對(duì)應(yīng)的SQL語(yǔ)句是標(biāo)準(zhǔn)的INSERT命令

?? */

??LOGD("Insert one personal info into personal_info table");

?? #defineINSERT_PERSONAL_INFO? \

??"INSERT INTO personal_info"\

??"(name,age,sex)"\

??"VALUES"\

??"(?,?,?)"?? //注意這一行語(yǔ)句中的問(wèn)號(hào)

??LOGD("\t%s\n",INSERT_PERSONAL_INFO);

?

?? //sqlite3_stmt是SQLite中很重要的一個(gè)結(jié)構(gòu)體,它代表了一條SQL語(yǔ)句

?? sqlite3_stmt* pstmt = NULL;

?? /*

??? 調(diào)用sqlite3_prepare初始化pstmt,并將其和INSERT_PERSONAL_INFO綁定。

??? 也就是說(shuō),如果執(zhí)行pstmt,就會(huì)執(zhí)行INSERT_PERSONAL_INFO語(yǔ)句

?? */

?? ret =sqlite3_prepare(g_pDBHandle,INSERT_PERSONAL_INFO,-1,&pstmt,NULL);

??CHECK_DB_ERR(ret,SQLITE_OK);

? /*

??? 調(diào)用sqlite3_bind_xxx為該pstmt中對(duì)應(yīng)的問(wèn)號(hào)綁定具體的值,該函數(shù)的第二個(gè)參數(shù)用于

??? 指定第幾個(gè)問(wèn)號(hào)

? */

?? 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);

?? //調(diào)用sqlite3_step執(zhí)行對(duì)應(yīng)的SQL語(yǔ)句,該函數(shù)如果執(zhí)行成功,我們的personal_info

?? //表中將添加一條新記錄,對(duì)應(yīng)值為(1,dengfanping,30,male)

?? ret =sqlite3_step(pstmt);

??CHECK_DB_ERR(ret,SQLITE_DONE);

?? //調(diào)用sqlite3_finalize銷(xiāo)毀該SQL語(yǔ)句

?? ret =sqlite3_finalize(pstmt);

??

?? //下面將從表中查詢(xún)name為"dengfanping"的person的age值

??LOGD("select dengfanping's age from personal_info table");

?? pstmt =NULL;

?? /*

??? 重新初始化該pstmt,并將其和SQL語(yǔ)句“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中第一個(gè)問(wèn)號(hào)為字符串“dengfanping”,最終該SQL語(yǔ)句為

??? SELECTage FROM personal_info WHERE name = 'dengfanping'

?? */

?? ret =sqlite3_bind_text(pstmt,1,"dengfanp