<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>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                還記得Settings數據庫嗎?SystemServer中很多Service都需要向它查詢配置信息。為此,Android提供了一個SettingsProvider來幫助開發者。該Provider在SettingsProvider.apk中,installSystemProviders就會加載該APK并把SettingsProvider放到SystemServer進程中來運行。 此時的SystemServer已經加載了framework-res.apk,現在又要加載另外一個APK文件,這就是多個APK運行在同一進程的典型案例。另外,通過installSystemProviders函數還能見識ContentProvider的安裝過程,下面就來分析它。 * * * * * **提示**:讀者在定制自己的Android系統時,萬不可去掉/system/app/SettingsProvider.apk,否則系統將無法正常啟動。 * * * * * **ActivityManagerService.java::installSystemProviders** ~~~ public static final void installSystemProviders(){ List<ProviderInfo> providers; synchronized (mSelf) { /* 從mProcessNames找到進程名為“system”且uid為SYSTEM_UID的ProcessRecord, 返回值就是前面在installSystemApplication中創建的那個ProcessRecord,它代表 SystemServer進程 */ ProcessRecord app = mSelf.mProcessNames.get("system",Process.SYSTEM_UID); //①關鍵調用,見下文分析 providers= mSelf.generateApplicationProvidersLocked(app); if(providers != null) { ......//將非系統APK(即未設ApplicationInfo.FLAG_SYSTEM標志)提供的Provider //從providers列表中去掉 } if(providers != null) {//②為SystemServer進程安裝Provider mSystemThread.installSystemProviders(providers); } //監視Settings數據庫中Secure表的變化,目前只關注long_press_timeout配置的變化 mSelf.mCoreSettingsObserver = new CoreSettingsObserver(mSelf); //UsageStatsService的工作,以后再討論 mSelf.mUsageStatsService.monitorPackages(); } ~~~ 在代碼中列出了兩個關鍵調用,分別是: - 調用generateApplicationProvidersLocked函數,該函數返回一個ProviderInfo List。 - 調用ActivityThread的installSystemProviders函數。ActivityThread可以看做是進程的Android運行環境,那么installSystemProviders表示為該進程安裝ContentProvider。 * * * * * **注意**:此處不再區分系統進程還是應用進程。由于只和ActivityThread交互,因此它運行在什么進程無關緊要。 * * * * * 下面來看第一個關鍵點generateApplicationProvidersLocked函數。 1. AMS的 generateApplicationProvidersLocked函數分析 **ActivityManagerService.java::generateApplicationProvidersLocked** ~~~ private final List<ProviderInfo> generateApplicationProvidersLocked( ProcessRecordapp) { List<ProviderInfo> providers = null; try { //①向PKMS查詢滿足要求的ProviderInfo,最重要的查詢條件包括:進程名和進程uid providers = AppGlobals.getPackageManager(). queryContentProviders(app.processName, app.info.uid, STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS); } ...... if(providers != null) { finalint N = providers.size(); for(int i=0; i<N; i++) { //②AMS對ContentProvider的管理,見下文解釋 ProviderInfo cpi = (ProviderInfo)providers.get(i); ComponentName comp = new ComponentName(cpi.packageName, cpi.name); ContentProviderRecord cpr = mProvidersByClass.get(comp); if(cpr == null) { cpr = new ContentProviderRecord(cpi, app.info, comp); //ContentProvider在AMS中用ContentProviderRecord來表示 mProvidersByClass.put(comp, cpr);//保存到AMS的mProvidersByClass中 } //將信息也保存到ProcessRecord中 app.pubProviders.put(cpi.name, cpr); //保存PackageName到ProcessRecord中 app.addPackage(cpi.applicationInfo.packageName); //對該APK進行dex優化 ensurePackageDexOpt(cpi.applicationInfo.packageName); } } returnproviders; } ~~~ 由以上代碼可知:generateApplicationProvidersLocked先從PKMS那里查詢滿足條件的ProviderInfo信息,而后將它們分別保存到AMS和ProcessRecord中對應的數據結構中。 先看查詢函數queryContentProviders。 (1) PMS中 queryContentProviders函數分析 **PackageManagerService.java::queryContentProviders** ~~~ public List<ProviderInfo>queryContentProviders(String processName, int uid,int flags) { ArrayList<ProviderInfo> finalList = null; synchronized (mPackages) { //還記得mProvidersByComponent的作用嗎?它以ComponentName為key,保存了 //PKMS掃描APK得到的PackageParser.Provider信息。讀者可參考圖4-8 finalIterator<PackageParser.Provider> i = mProvidersByComponent.values().iterator(); while(i.hasNext()) { final PackageParser.Provider p = i.next(); //下面的if語句將從這些Provider中搜索本例設置的processName為“system”, //uid為SYSTEM_UID,flags為FLAG_SYSTEM的Provider if (p.info.authority != null && (processName == null ||(p.info.processName.equals(processName) &&p.info.applicationInfo.uid == uid)) && mSettings.isEnabledLPr(p.info, flags) && (!mSafeMode || ( p.info.applicationInfo.flags &ApplicationInfo.FLAG_SYSTEM) != 0)) { if (finalList == null) { finalList = newArrayList<ProviderInfo>(3); } //由PackageParser.Provider得到ProviderInfo,并添加到finalList中 //關于Provider類及ProviderInfo類,可參考圖4-5 finalList.add(PackageParser.generateProviderInfo(p,flags)); } } } if(finalList != null) //最終結果按provider的initOrder排序,該值用于表示初始化ContentProvider的順序 Collections.sort(finalList, mProviderInitOrderSorter); returnfinalList;//返回最終結果 } ~~~ queryContentProviders函數很簡單,就是從PKMS那里查找滿足條件的Provider,然后生成AMS使用的ProviderInfo信息。為何偏偏能找到SettingsProvider呢?來看它的AndroidManifest.xml文件,如圖6-7所示。 :-: ![](http://img.blog.csdn.net/20150803122756437?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖6-7 SettingsProvider的AndroidManifest.xml文件示意 由圖6-7可知,SettingsProvider設置了其uid為“android.uid.system”,同時在application中設置了process名為“system”。而在framework-res.apk中也做了相同的設置。所以,現在可以確認SettingsProvider將和framework-res.apk運行在同一個進程,即SystemServer中。 提示從運行效率角度來說,這樣做也是合情合理的。因為SystemServer的很多Service都依賴Settings數據庫,把它們放在同一個進程中,可以降低由于進程間通信帶來的效率損失。 (2) 關于ContentProvider的介紹 前面介紹的從PKMS那里查詢到的ProviderInfo還屬于公有財產,現在我們要將它與AMS及ProcessRecord聯系起來。 - AMS保存ProviderInfo的原因是它要管理ContentProvider。 - ProcessRecord保存ProviderInfo的原因是ContentProvider最終要落實到一個進程中。其實也是為了方便AMS管理,例如該進程一旦退出,AMS需要把其中的ContentProvider信息從系統中去除。 AMS及ProcessRecord均使用了一個新的數據結構ContentProviderRecord來管理ContentProvider信息。圖6-8展示了ContentProviderRecord相應的數據結構。 :-: ![](http://img.blog.csdn.net/20150803122817345?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖6-8 ContentProvicerRecord及相應的“管理團隊” 由圖6-8可知: - ContentProviderRecord從ContentProviderHolder派生,內部保存了ProviderInfo、該Provider所駐留的進程ProcessRecord,以及使用該ContentProvider的客戶端進程ProcessRecord(即clients成員變量)。 - AMS的mProviderByClass成員變量及ProcessRecord的pubProviders成員變量均以ComponentName為Key來保存對應的ContentProviderRecord對象。 至此,Provider信息已經保存到AMS及ProcessRecord中了。那么,下一步的工作是什么呢? 2. ActivityThread 的installSystemProviders函數分析 在AMS和ProcessRecord中都保存了Provider信息,但這些僅僅都是一些信息,并不是ContentProvider,因此下面要創建一個ContentProvider實例(即SettingsProvider對象)。該工作由ActivityThread的installSystemProviders來完成,代碼如下: **ActivityThread.java::installSystemProviders** ~~~ public final void installSystemProviders(List<ProviderInfo>providers) { if(providers != null) //調用installContentProviders,第一個參數真實類型是Application installContentProviders(mInitialApplication, providers); } ~~~ installContentProviders這個函數是所有ContentProvider產生的必經之路,其代碼如下: **ActivityThread.java::installContentProviders** ~~~ private void installContentProviders( Context context, List<ProviderInfo> providers) { finalArrayList<IActivityManager.ContentProviderHolder> results = new ArrayList<IActivityManager.ContentProviderHolder>(); Iterator<ProviderInfo> i = providers.iterator(); while(i.hasNext()) { ProviderInfo cpi = i.next(); //①調用installProvider函數,得到一個IContentProvider對象 IContentProvider cp = installProvider(context, null, cpi, false); if (cp!= null) { IActivityManager.ContentProviderHolder cph = newIActivityManager.ContentProviderHolder(cpi); cph.provider = cp; //將返回的cp保存到results數組中 results.add(cph); synchronized(mProviderMap){ //mProviderRefCountMap,;類型為HashMap<IBinder,ProviderRefCount>, //主要通過ProviderRefCount對ContentProvider進行引用計數控制,一旦引用計數 //降為零,表示系統中沒有地方使用該ContentProvider,要考慮從系統中注銷它 mProviderRefCountMap.put(cp.asBinder(), new ProviderRefCount(10000)); } } } try { //②調用AMS的publishContentProviders注冊這些ContentProvider,第一個參數 //為ApplicationThread ActivityManagerNative.getDefault().publishContentProviders( getApplicationThread(), results); } ...... } ~~~ installContentProviders實際上是標準的ContentProvider安裝時調用的程序。安裝ContentProvider包括兩方面的工作: - 先在ActivityThread中通過installProvider得到一個ContentProvider實例。 - 向AMS發布這個ContentProvider實例。如此這般,一個APK中聲明的ContentProvider才能登上歷史舞臺,發揮其該有的作用。 * * * * * **提示** :上述工作其實和Binder Service類似,一個Binder Service也需要先創建,然后注冊到ServiceManager中。 * * * * * 馬上來看ActivityThread的installProvider函數。 (1) ActivityThread的installProvider函數分析 **ActivityThread.java::installProvider** ~~~ private IContentProvider installProvider(Contextcontext, IContentProvider provider, ProviderInfoinfo, boolean noisy) { //注意本例所傳的參數:context為mInitialApplication,provider為null,info不為null, // noisy為false ContentProvider localProvider = null; if(provider == null) { Context c = null; ApplicationInfo ai = info.applicationInfo; /* 下面這個if判斷的作用就是為該ContentProvider找到對應的Application。 在AndroidManifest.xml中,ContentProvider是Application的子標簽,所以 ContentProvider和Application有一種對應關系。在本例中,傳入的context( 其實是mInitialApplication)代表的是framework-res.apk,而Provider代表的 是SettingsProvider。而SettingsProvider.apk所對應的Application還未創建, 所以下面的判斷語句最終會進入最后的else分支 */ if(context.getPackageName().equals(ai.packageName)) { c= context; }else if (mInitialApplication != null && mInitialApplication.getPackageName().equals(ai.packageName)){ c = mInitialApplication; } else { try{ //ai.packageName應該是SettingsProvider.apk的Package, //名為“com.android.providers.settings” //下面將創建一個Context,指向該APK c = context.createPackageContext(ai.packageName, Context.CONTEXT_INCLUDE_CODE); } }//if(context.getPackageName().equals(ai.packageName))判斷結束 if (c == null) return null; try { /* 為什么一定要找到對應的Context呢?除了ContentProvider和Application的 對應關系外,還有一個決定性原因:即只有對應的Context才能加載對應APK的Java字節碼, 從而可通過反射機制生成ContentProvider實例 */ finaljava.lang.ClassLoader cl = c.getClassLoader(); //通過Java反射機制得到真正的ContentProvider, //此處將得到一個SettingsProvider對象 localProvider=(ContentProvider)cl.loadClass(info.name).newInstance(); //從ContentProvider中取出其mTransport成員(見下文分析) provider =localProvider.getIContentProvider(); if (provider == null) return null; //初始化該ContentProvider,內部會調用其onCreate函數 localProvider.attachInfo(c, info); }...... }//if(provider == null)判斷結束 synchronized (mProviderMap) { /* ContentProvider必須指明一個和多個authority,在第4章曾經提到過, 在URL中host:port的組合表示一個authority。這個單詞不太好理解,可簡單 認為它用于指定ContentProvider的位置(類似網站的域名) */ String names[] =PATTERN_SEMICOLON.split(info.authority); for (int i=0; i<names.length; i++) { ProviderClientRecord pr = newProviderClientRecord(names[i], provider, localProvider); try { //下面這句對linkToDeath的調用頗讓人費解,見下文分析 provider.asBinder().linkToDeath(pr, 0); mProviderMap.put(names[i], pr); }...... }//for循環結束 if(localProvider != null) { // mLocalProviders用于存儲由本進程創建的ContentProvider信息 mLocalProviders.put(provider.asBinder(), new ProviderClientRecord(null, provider, localProvider)); } }//synchronized 結束 return provider; } ~~~ 以上代碼不算復雜,但是涉及一些數據結構和一句令人費解的對inkToDeath函數的調用。先來說說那句令人費解的調用。 在本例中,provider變量并非通過函數參數傳入,而是在本進程內部創建的。provider在本例中是Bn端(后面分析ContentProvider的getIContentProvider時即可知道),Bn端進程為Bn端設置死亡通知本身就比較奇怪。如果Bn端進程死亡,它設置的死亡通知也無法發送給自己。幸好源代碼中有句注釋:“Cache the pointer for the remote provider”。意思是如果provider參數是通過installProvider傳遞過來的(即該Provider代表遠端進程的ContentProvider,此時它應為Bp端),那么這種處理是合適的。不管怎樣,這僅僅是為了保存pointer,所以也無關宏旨。 至于代碼中涉及的數據結構,我們整理為如圖6-9所示。 :-: ![](http://img.blog.csdn.net/20150803122848097?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖6-9 ActivityThread中ContentProvider涉及的數據結構 由圖6-9可知: - ContentProvider類本身只是一個容器,而跨進程調用的支持是通過內部類Transport實現的。Transport從ContentProviderNative派生,而ContentProvider的成員變量mTransport指向該Transport對象。ContentProvider的getIContentProvider函數即返回mTransport成員變量。 - ContentProviderNative從Binder派生,并實現了IContentProvider接口。其內部類ContentProviderProxy是供客戶端使用的。 - ProviderClientRecord是ActivityThread提供的用于保存ContentProvider信息的一個數據結構。它的mLocalProvider用于保存ContentProvider對象,mProvider用于保存IContentProvider對象。另外一個成員mName用于保存該ContentProvider的一個authority。注意,ContentProvider可以定義多個authority,就好像一個網站有多個域名一樣。 至此,本例中的SettingProvider已經創建完畢,接下來的工作就是把它推向歷史舞臺——即發布該Provider。 (2) AMS的 publishContentProviders分析 publicContentProviders函數用于向AMS注冊ContentProviders,其代碼如下: **ActivityManagerService.java::publishContentProviders** ~~~ publicfinal void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder>providers) { ...... synchronized(this){ //找到調用者所在的ProcessRecord對象 finalProcessRecord r = getRecordForAppLocked(caller); ...... finallong origId = Binder.clearCallingIdentity(); final intN = providers.size(); for (inti=0; i<N; i++) { ContentProviderHolder src = providers.get(i); ...... //①注意:先從該ProcessRecord中找對應的ContentProviderRecord ContentProviderRecord dst = r.pubProviders.get(src.info.name); if(dst != null) { ComponentName comp = newComponentName(dst.info.packageName, dst.info.name); //以ComponentName為key,保存到mProvidersByClass中 mProvidersByClass.put(comp, dst); String names[] = dst.info.authority.split(";"); for (int j = 0; j < names.length; j++) mProvidersByName.put(names[j], dst);//以authority為key,保存 //mLaunchingProviders用于保存處于啟動狀態的Provider int NL = mLaunchingProviders.size(); int j; for (j=0; j<NL; j++) { if (mLaunchingProviders.get(j) ==dst) { mLaunchingProviders.remove(j); j--; NL--; }// }//for (j=0; j<NL; j++)結束 synchronized (dst) { dst.provider = src.provider; dst.proc = r; dst.notifyAll(); }//synchronized結束 updateOomAdjLocked(r);//每發布一個Provider,需要調整對應進程的oom_adj }//for(int j = 0; j < names.length; j++)結束 }//for(int i=0; i<N; i++)結束 Binder.restoreCallingIdentity(origId); }// synchronized(this)結束 } ~~~ 這里應解釋一下publishContentProviders的工作流程: - 先根據調用者的pid找到對應的ProcessRecord對象。 - 該ProcessRecord的pubProviders中保存了ContentProviderRecord信息。該信息由前面介紹的AMS的generateApplicationProvidersLocked函數根據Package本身的信息生成。此處將判斷要發布的ContentProvider是否由該Package聲明。 - 如果判斷返回成功,則將該ContentProvider及其對應的authority加到mProvidersByName中。注意,AMS中還有一個mProvidersByClass變量,該變量以ContentProvider的ComponentName為key,即系統提供多種方式找到某一個ContentProvider,一種是通過 authority,另一種方式就是指明ComponentName。 - mLaunchingProviders和最后的notifyAll函數用于通知那些等待ContentProvider所在進程啟動的客戶端進程。例如,進程A要查詢一個數據庫,需要通過進程B中的某個ContentProvider 來實施。如果B還未啟動,那么AMS就需要先啟動B。在這段時間內,A需要等待B啟動并注冊對應的ContentProvider。B一旦完成注冊,就需要告知A退出等待以繼續后續的查詢工作。 現在,一個SettingsProvider就算正式在系統中掛牌并注冊了,此后,和Settings數據庫相關的操作均由它來打理。 3. AMS的installSystemProviders總結 AMS的installSystemProviders函數其實就是用于啟動SettingsProvider,其中比較復雜的是ContentProvider相關的數據結構,讀者可參考圖6-9。
                  <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>

                              哎呀哎呀视频在线观看