Android源码分析: 应用启动安装ContentProvider分析
ContentProvider是Android应用开发的四大组件之一,并且源码相对于其他几个也是比较简单的。因此我们先来看看它的源码。ContentProvider的使用我们会涉及到外部程序调用应用的ContentProvider来查询数据,也有监听数据的变化,以及ContentProvider的安装。我们先来看安装部分的源码。 ContentProvider根据它的名字就知道,他是一个内容提供者,它提供了整删改查的接口,方便Android应用跨应用跨进程的数据共享,在Android系统中,相册,通讯录等等都是通过ContentProvider来共享数据让其他应用可以使用。看源码,我们需要关注两个点,一个是ContentProvider如何安装的,另一个就是当我们发起一个查询的时候,是怎样和内容提供的那个进程进行交互的。 ContentProvider的安装触发通常有两个场景,一是外部程序需要使用ContentProvider的时候,另一个是在应用Application启动的时候,这个一般触发的场景有,启动Service,启动Activity。这所有场景的共同点都是拉起进程,初始化Application。这两大场景来安装ContentProvider除了开始的路径会有差别,后面的部分大致都相等。因此我们这里以启动App同时安装Provider作为分析路径。 ContentProvider相关的类有如下这些: classDiagram class IContentProvider <<interface>> IContentProvider namespace SYSTEM_SERVER进程 { class ContentProviderRecord class ProviderMap class ProcessProviderRecord } namespace 应用进程 { class ContentProvider class ContentProviderNative class Transport class ProviderClientRecord class Binder } <<abstract>> ContentProvider class ProviderInfo <<abstract>> ContentProviderNative ContentProviderRecord *-- IContentProvider ContentProviderRecord *-- ProviderInfo IContentProvider <|.. ContentProviderNative Binder <|-- ContentProviderNative ContentProviderNative <|-- Transport ContentProvider *-- Transport class ContentProviderHolder ContentProviderHolder *-- IContentProvider ProviderClientRecord *-- IContentProvider ProviderClientRecord *-- ContentProvider ProviderClientRecord *-- ContentProviderHolder ContentProviderHolder *-- ProviderInfo ProviderMap o-- ContentProviderRecord ProcessProviderRecord o-- ContentProviderRecordPackageManager解析出来的Provider信息通过ProviderInfo来保存,我们平时创建的ContentProvider它有一个内部类Transport,在它里面实现了Binder的客户端和服务端。通过它的Binder代理,AMS进程能够执行ContentProvider的增删改查,这不是本文的重点,下次再说。在App进程内,除了我们的ContentProvider对象,还会构建ProviderClientRecord对象,ContentProvider和它的binder对象和ProviderInfo信息会存在这个对象中。在服务端也有一个对应的ContentProviderRecord对象,里面存储了IcontentProvider binder对象和ProviderInfo。并且客户端和服务端的这些record都会存到Map中去,这样方便后续调用的时候查找。而构建所有这些record和把他们放入到Map中的过程,其实就是Provider的安装过程。 我们开始看代码,在应用进程创建完成之后,AMS会执行attachApplicationLocked从而来创建App的Application对象,因此我们从这里开始看,而进程的启动可以挖个坑以后再写。最初起点应该在ActivityManagerService的attachApplicationLocked方法中: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info); List<ProviderInfo> providers = normalMode ? mCpHelper.generateApplicationProvidersLocked(app) : null; ... final ProviderInfoList providerList = ProviderInfoList.fromList(providers); thread.bindApplication(processName, appInfo, app.sdkSandboxClientAppVolumeUuid, app.sdkSandboxClientAppPackage, providerList, null, profilerInfo, null, null, null, testMode, mBinderTransactionTrackingEnabled, enableTrackAllocation, isRestrictedBackupMode || !normalMode, app.isPersistent(), new Configuration(app.getWindowProcessController().getConfiguration()), app.getCompat(), getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), buildSerial, autofillOptions, contentCaptureOptions, app.getDisabledCompatChanges(), serializedSystemFontMap, app.getStartElapsedTime(), app.getStartUptime()); 可以看到AMS中首先是通过mCpHelper去生成当前应用的Provider列表,之后调用应用进程的bindApplication的时候再带过去。mCpHelper是一个ContentProviderHelper对象,我们先来看看它的generateApplicationProvidersLocked方法: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) { final List<ProviderInfo> providers; providers = AppGlobals.getPackageManager().queryContentProviders( app.processName, app.uid, ActivityManagerService.STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS | PackageManager.MATCH_DIRECT_BOOT_AUTO, /*metaDataKey=*/ null) .getList(); int numProviders = providers.size(); final ProcessProviderRecord pr = app.mProviders; pr.ensureProviderCapacity(numProviders + pr.numberOfProviders()); for (int i = 0; i < numProviders; i++) { // NOTE: keep logic in sync with installEncryptionUnawareProviders ProviderInfo cpi = providers.get(i); boolean singleton = mService.isSingleton(cpi.processName, cpi.applicationInfo, cpi.name, cpi.flags); if (singleton && app.userId != UserHandle.USER_SYSTEM) { // This is a singleton provider, but a user besides the // default user is asking to initialize a process it runs // in... well, no, it doesn't actually run in this process, // it runs in the process of the default user. Get rid of it. providers.remove(i); numProviders--; i--; continue; } final boolean isInstantApp = cpi.applicationInfo.isInstantApp(); final boolean splitInstalled = cpi.splitName == null || ArrayUtils.contains( cpi.applicationInfo.splitNames, cpi.splitName); if (isInstantApp && !splitInstalled) { // For instant app, allow provider that is defined in the provided split apk. // Skipping it if the split apk is not installed. providers.remove(i); numProviders--; i--; continue; } ComponentName comp = new ComponentName(cpi.packageName, cpi.name); ContentProviderRecord cpr = mProviderMap.getPro
以上内容来自博客订阅源,版权归原作者。关注作者后可以在 关注列表 第一时间查看作者文章。

登录后,加入这场讨论。
登录参与