<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                第二個關鍵點是在MediaProvider客戶端示例中所調用的MediaStore.Image.Media 的query函數。MediaStore是多媒體開發中常用的類,其內部定義了專門針對Image、Audio、Video等不同多媒體信息的內部類來幫助客戶端開發人員更好底和MediaProvider交互。這些類及相互之間的關系如圖7-1所示。 :-: ![](http://img.blog.csdn.net/20150803130759625?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖7-1 MediaStore類圖 由圖7-1可知,MediaStore定義了較多的內部類,我們重點展示作為內部類之一的Image的情況,其中: - MediaColumns定義了所有與媒體相關的數據庫表都會用到的數據庫字段,而ImageColumns定義了單獨針對Image的數據庫字段。 - Image類定義了一個名為Media的內部類用于查詢和Image相關的信息,同時Image類還定義了一個名為Thumbnails的內部類用于查詢和Image相關的縮略圖的信息(在Android平臺上,縮略圖的來源有兩種,一種是Image,另一種是Video,故Image定義了名為Thumbnails的內部類,而Video也定義了一個名為Thumbnails的內部類)。 * * * * * **提示**:MediaStore類較為復雜,主要原因是它定義了一些同名類。讀者閱讀代碼時務須仔細。 * * * * * 下面來看Image.Media的query函數,其代碼非常簡單,如下所示: **MediaStore.java::Image.Media.query** ~~~ public static final class Media implementsImageColumns { public static final Cursor query(ContentResolvercr,Uri uri, String[]projection) { //直接調用ContentResolver的query函數 returncr.query(uri, projection, null, null,DEFAULT_SORT_ORDER); } ~~~ Image.Media的query函數直接調用ContentResolver的query函數,雖然cr的真實類型是ApplicationContentResolver,但是此函數卻由其基類ContentResolver實現。 * * * * * **提示**:追求運行效率的程序員也許會對上邊這段代碼的實現略有微詞,因為Image.Media的query函數基本上沒做任何有意義的工作。假如客戶端直接調用cr.query函數,則此處的query就增加了一次函數調用和返回的開銷(即Image.Media query調用和返回時參數的入棧/出棧)。但是,通過Image.Media的封裝將使程序更清晰和易讀(與直接使用ContentResolver的query相比,代碼閱讀者一看Image.Media就知道其query函數應該和Image有關,否則需要通過解析uri參數才能確定查詢的信息是什么)。代碼清晰易讀和運行效率高,往往是軟件開發中的熊掌和魚,它們之間的對立性,將在本章中體現得淋漓盡致。筆者建議讀者在實際開發中結合具體情況決定取舍,萬不可鉆牛角尖。 * * * * * 1. ContentResolver的query函數分析 **ContentResolver.java::query** ~~~ public final Cursor query(Uri uri, String[]projection, String selection, String[] selectionArgs, String sortOrder) { //調用acquireProvider函數,參數為uri,函數也由ContentResolver實現 IContentProvider provider = acquireProvider(uri); //注意:下面將和ContentProvider交互, 相關知識留待7.4節再分析 ...... } ~~~ ContentResolver的query將調用acquireProvider,該函數定義在ContentResolver類中,代碼如下: **ContentResolver.java::query** ~~~ public final IContentProvider acquireProvider(Uriuri) { if(!SCHEME_CONTENT.equals(uri.getScheme())) return null; Stringauth = uri.getAuthority(); if (auth!= null) { //acquireProvider是一個抽象函數,由ContentResolver的子類實現。在本例中,該函數 //將由ApplicationContentResolver實現。uri.getAuthority將返回代表目標 //ContentProvider的名字 returnacquireProvider(mContext, uri.getAuthority()); } returnnull; } ~~~ 如上所述,acquireProvider由ContentResolver的子類實現,在本例中該函數由ApplicationContentResolver定義,代碼如下: **ContextImpl.java::acquireProvider** ~~~ protected IContentProvider acquireProvider(Contextcontext, String name) { //mMainThread指向代表應用進程主線程的ActivityThread對象,每個應用進程只有一個 //ActivityThread對象 return mMainThread.acquireProvider(context,name); } ~~~ 如以上代碼所示,最終ActivityThread的acquireProvider函數將被調用,希望它不要再被層層轉包了。 2. AcitvityThread的acquireProvider函數分析 ActivityThread的 acquireProvider函數的代碼如下: **ActivityThread.java::acquireProvider** ~~~ public final IContentProvideracquireProvider(Context c, String name) { //①調用getProvider函數,它很重要。見下文分析 IContentProvider provider = getProvider(c,name); ...... IBinderjBinder = provider.asBinder(); synchronized(mProviderMap) { //客戶端進程將本進程使用的ContentProvider信息保存到mProviderRefCountMap中, //其主要功能與引用計數和資源釋放有關,讀者暫可不理會它 ProviderRefCount prc = mProviderRefCountMap.get(jBinder); if(prc== null) mProviderRefCountMap.put(jBinder, newProviderRefCount(1)); else prc.count++; } returnprovider; } ~~~ 在acquireProvider內部調用getProvider得到一個IContentProvider類型的對象,該函數非常重要,其代碼為: **ActivityThread.java::getProvider** ~~~ private IContentProvider getProvider(Contextcontext, String name) { /* 查詢該應用進程是否已經保存了用于和遠端ContentProvider通信的對象existing。 此處,我們知道existing的類型是IContentProvider,不過IContentProvider是一 個interface,那么existing的真實類型是什么呢?稍后再揭示 */ IContentProvider existing = getExistingProvider(context, name); if(existing != null) return existing;//如果existing存在,則直接返回 IActivityManager.ContentProviderHolder holder = null; try { //如果existing不存在,則需要向AMS查詢,返回值的類型為ContentProviderHolder holder= ActivityManagerNative.getDefault().getContentProvider( getApplicationThread(), name); }...... //注意:記住下面這個函數調用,此時是在客戶端進程中 IContentProvider prov = installProvider(context, holder.provider, holder.info, true); ...... returnprov; } ~~~ 以上代碼中讓人比較頭疼的是其中新出現的幾種數據類型,如IContentProvider、ContentProviderHolder。先來分析AMS的getContentProvider。 3. AMS的getContentProvider函數分析 getContentProvider的功能主要由getContentProviderImpl函數實現,故此處可直接對它進行分析。 (1) getContentProviderImpl啟動目標進程 getContentProviderImpl函數較長,可分段來看,先來分析下面一段代碼。 **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)......//如果查詢不到調用者信息,則拋出SecurityException }// if (caller != null)判斷結束 //name參數為調用進程指定的代表目標ContentProvider的authority cpr =mProvidersByName.get(name); //如果cpr不為空,表明該ContentProvider已經在AMS中注冊 booleanproviderRunning = cpr != null; if(providerRunning){ ......//如果該ContentProvider已經存在,則進行對應處理, 相關內容可自行閱讀 } //如果目標ContentProvider對應進程還未啟動 if(!providerRunning) { try { //查詢PKMS,得到指定的ProviderInfo信息 cpi = AppGlobals.getPackageManager().resolveContentProvider( name,STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS); }...... String msg; //權限檢查,此處不作討論 if((msg=checkContentProviderPermissionLocked(cpi, r)) != null) throw new SecurityException(msg); /* 如果system_server還沒啟動完畢,并且該ContentProvider不運行在system_server 中,則此時不允許啟動ContentProvider。讀者還記得哪個ContentProvider運行在 system_server進程中嗎?答案是SettingsProvider */ ....... ComponentName comp = new ComponentName(cpi.packageName, cpi.name); cpr =mProvidersByClass.get(comp); finalboolean firstClass = cpr == null; //初次啟動MediaProvider對應進程時,firstClass一定為true if (firstClass) { try { //查詢PKMS,得到MediaProvider所在的Application信息 ApplicationInfoai = AppGlobals.getPackageManager().getApplicationInfo( cpi.applicationInfo.packageName, STOCK_PM_FLAGS); if (ai == null) return null; //在AMS內部通過ContentProviderRecord來保存ContentProvider的信息,類似 //ActivityRecord,BroadcastRecord等 cpr = new ContentProviderRecord(cpi, ai,comp); }...... }// if(firstClass)判斷結束 ~~~ 以上代碼的邏輯比較簡單,主要是為目標ContentProvider(即MediaProvider)創建一個ContentProviderRecord對象。結合第6章的知識,AMS為四大組件都設計了對應的數據結構,如ActivityRecord、BroadcastRecord等。 接著看getContentProviderImpl,其下一步的工作就是啟動目標進程: **ActivityManagerService.java::getContentProviderImpl** ~~~ /* canRunHere函數用于判斷目標CP能否運行在r所對應的進程(即調用者所在進程) 該函數內部做如下判斷: (info.multiprocess|| info.processName.equals(app.processName)) && (uid == Process.SYSTEM_UID || uid == app.info.uid) 就本例而言,MediaProvider不能運行在客戶端進程中 */ if (r !=null && cpr.canRunHere(r)) returncpr; finalint N = mLaunchingProviders.size(); ......//查找目標ContentProvider對應的進程是否正處于啟動狀態 //如果i大于等于N,表明目標進程的信息不在mLaunchingProviders中 if (i>= N) { finallong origId = Binder.clearCallingIdentity(); ...... //調用startProcessLocked函數創建目標進程 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; //將該進程信息保存到mLaunchingProviders中 mLaunchingProviders.add(cpr); } if(firstClass) mProvidersByClass.put(comp, cpr); mProvidersByName.put(name, cpr); /* 下面這個函數將為客戶端進程和目標CP進程建立緊密的關系,即當目標CP進程死亡后, AMS將根據該函數建立的關系找到客戶端進程并殺死(kill)它們。在7.2.3節 有對這個函數的相關解釋 */ incProviderCount(r, cpr); if(cpr.launchingApp == null) return null; try { cpr.wait();//等待目前進程的啟動 } ...... }// synchronized(this)結束 returncpr; } ~~~ 通過對以上代碼的分析發現,getContentProviderImpl將等待一個事件,想必讀者也能明白,此處一定是在等待目標進程啟動并創建好MediaProvider。目標進程的這部分工作用專業詞語來表達就是發布目標ContentProvider(即本例的MediaProvider)。 (2) MediaProvider的創建 根據第6章的介紹,目標進程啟動后要做的第一件大事就是調用AMS的attachApplication函數,該函數的主要功能由attachApplicationLocked完成。我們回顧一下相關代碼。 **ActivityManagerService.java::attachApplicationLocked** ~~~ private final booleanattachApplicationLocked(IApplicationThread thread, int pid) { ...... //通過PKMS查詢運行在該進程中的CP信息,并保存到mProvidersByClass中 Listproviders = normalMode ? generateApplicationProvidersLocked(app) : null; //調用目標應用進程的bindApplication函數,此處將providers信息傳遞給目標進程 thread.bindApplication(processName,appInfo, providers, app.instrumentationClass, profileFile, ......); ...... } ~~~ 再來看目標進程bindApplication的實現,其內部最終會通過handleBindApplication函數處理,我們回顧一下相關代碼。 **ActivtyThread.java::handleBindApplication** ~~~ private void handleBindApplication(AppBindDatadata) { ...... if(!data.restrictedBackupMode){ List<ProviderInfo> providers = data.providers; if(providers != null) { //調用installContentProviders安裝 installContentProviders(app, providers); ...... } } ...... } ~~~ AMS傳遞過來的ProviderInfo列表將由目標進程的installContentProviders處理,其相關代碼如下: **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()) { //①也調用installProvider,注意該函數傳遞的第二個參數為null IContentProvider cp = installProvider(context, null, cpi, false); if (cp!= null) { IActivityManager.ContentProviderHolder cph = newIActivityManager.ContentProviderHolder(cpi); cph.provider = cp; results.add(cph);//將信息添加到results數組中 ......//創建引用計數 } }//while循環結束 try { //②調用AMS的publishContentProviders發布ContentProviders ActivityManagerNative.getDefault().publishContentProviders( getApplicationThread(), results); } ...... } ~~~ 以上代碼列出了兩個關鍵點,分別是: - 調用installProvider得到一個IContentProvider類型的對象。 - 調用AMS的publishContentProviders發布本進程所運行的ContentProvider。該函數留到后面再作分析 在繼續分析之前,筆者要特別強調installProvider,該函數既在客戶端進程中被調用(還記得7.2.2節ActivityThread的acquireProvider函數中那句注釋嗎?),又在目標進程(即此處MediaProvider所在進程)中被調用。與客戶端進程的調用相比,只在一處有明顯的不同: - 客戶端進程調用installProvider函數時,該函數的第二個參數不為null。 - 目標進程調用installProvider函數時,該函數的第二個參數硬編碼為null。 我們曾經在6.2.3分析過installProvider函數,結合那里的介紹可知:installProvider是一個通用函數,不論客戶端使用遠端的CP還是目標進程安裝運行在其上的CP上,最終都會調用它,只不過參數不同罷了。 來看installProvider函數,其代碼如下: **ActivityThread.java::installProvider** ~~~ private IContentProvider installProvider(Contextcontext, IContentProvider provider, ProviderInfo info, boolean noisy) { ContentProvider localProvider = null; if(provider == null) {//針對目標進程的情況 Context c = null; ApplicationInfo ai = info.applicationInfo; if(context.getPackageName().equals(ai.packageName)) { c = context; }......//這部分代碼已經在6.2.3節分析過了,其目的就是為了得到正確的 //Context用于加載Java字節碼 try{ final java.lang.ClassLoader cl = c.getClassLoader(); //通過Java反射機制創建MediaProvider實例 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)判斷結束 /* 由以上代碼可知,對于provider不為null 的情況(即客戶端調用的情況),該函數沒有 什么特殊的處理 */ ...... /* 引用計數及設置DeathReceipient等相關操作。在6.2.3節的第2個小標題中 曾說過,目標進程為自己進程中的CP實例設置DeathReceipient沒有作用,因為二者在同一個 進程中,自己怎么能接收自己的訃告消息呢?不過,如果客戶端進程為目標進程的CP設置 DeathReceipient又有作用嗎?仔細思考這個問題 */ returnprovider;//最終返回的對象是IContentProvider類型,它到底是什么呢? } ~~~ 由以代碼可知,installProvider最終返回的是一個IContentProvider類型的對象。對于目標進程而言,該對象是通過調用CP的實例對象的(本例就是MediaProvider) getIContentProvider函數得來的。而對于客戶端進程而言,該對象是由installProvider第二個參數傳遞進來的,那么,這個IContentProvider到底是什么? (3) IContentProvider的真面目 要說清楚IContentProvider,就先來看ContentProvider家族的類圖,如圖7-2所示。 :-: ![](http://img.blog.csdn.net/20150803130820677?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖7-2 ContentProvider類圖 圖7-2揭示了IContentProvider的真面目,具體介紹如下: - 每個ContentProvider實例中都有一個mTransport成員,其類型為Transport。 - Transport類從ContentProviderNative派生。由圖7-2可知,ContentProviderNative從Binder類派生,并實現了IContentProvider接口。結合前面的代碼,IContentProvider將是客戶端進程和目標進程交互的接口,即目標進程使用IContentProvider的Bn端Transport,而客戶端使用IContentProvider的Bp端,其類型是ContentProviderProxy(定義在ContentProviderNative.java中)。 客戶端如何通過IContentProvider query函數和目標CP進程交互的呢?其流程如下: - CP客戶端得到IContentProvider的Bp端(實際類型是ContentProviderProxy),并調用其query函數,在該函數內部將參數信息打包,傳遞給Transport(它是IContentProvider的Bn端)。 - Transport的onTransact函數將調用Transport的query函數,而Transport的query函數又將調用ContentProvider子類定義的query函數(即MediaProvider的query函數)。 關于目標進程這一系列的調用函數,不妨先看看Transport的query函數,其代碼為: **ContentProvider.java::Transport.query** ~~~ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { enforceReadPermission(uri); //Transport為ContentProvider的內部類,此處將調用ContentProvider的query函數 //本例中,該query函數由MediaProvider實現,故最終會調用MediaProvider的query returnContentProvider.this.query(uri, projection, selection, selectionArgs, sortOrder); } ~~~ 務必弄清楚,此處只有一個目標ContentProvider的實例,即只有一個MediaProvider對象。Transport的 query函數內部雖然調用的是基類ContentProvider的query函數,但是根據面向對象的多態原理,該函數最終由其子類(本例中是MediaProvider)來實現。 認識了IContentProvider,即知道了客戶端進程和目標進程的交互接口。 繼續我們的分析。此時目標進程需要將MediaProvider的信息通過AMS發布出去。 (4) AMS pulishContentProviders分析 要把目標進程的CP信息發布出去,需借助AMS 的pulishContentProviders函數,其代碼如下: **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) { ......//將相關信息分別保存到mProviderByClass和mProvidersByName中 int NL = mLaunchingProviders.size(); ...... //目標進程已經啟動,將其從mLaunchingProviders移除 synchronized (dst) { dst.provider = src.provider;//將信息保存在dst中 dst.proc = r; //觸發還等在(wait)getContentProvider中的那個客戶端進程 dst.notifyAll(); } updateOomAdjLocked(r);//調節目標進程的oom_adj等相關參數 }// if(dst != null)判斷結束 ...... } } ~~~ 至此,客戶端進程將從getContentProvider中返回,并調用installProvider函數。根據前面的分析,客戶端進程調用installProvider時,其第二個參數不為null,即客戶端進程已經從AMS中得到了能直接和目標進程交互的IContentProvider Bp端對象。此后,客戶端就可直接使用該對象向目標進程發起請求。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看