💎一站式轻松地调用各大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。