ContentProvider的启动流程分析
Posted zhenjie_chang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ContentProvider的启动流程分析相关的知识,希望对你有一定的参考价值。
ContentProvider是android系统的四大组件之一,主要用于向外部提供数据。不仅可以向自己应用进程提供数据,也可以向其他进程的提供数据。所以在分析ContentProvider的时候我们首先分析本进程的ContentProvider的启动过程,然后再分析调用其他进程的ContentProvider的时候ContentProvider的安装启动过程。
本进程ContentProvider启动过程分析
本进程内的ContentProvider一般是在进程启动的时候就启动并创建的。在Activity的启动过程中分析过新进程的启动过程。
1. 创建一个新的进程。调用ActivityThread的main方法
2. 调用ActivityThread的attach方法
3. 调用ActivityManagerService的attachApplication方法,通知新的进程创建完成,根据新创建的进程初始化ProcessRecord的信息。然后查询所有和本进程相关的ContentProvider信息。
4. 调用新建进程的bindApplication方法,通知新进程安装并启动这些ContentProvider
以上就是本进程的ContentProvider的启动和安装过程。第一第二步我们不再分析,直接从AMS服务的attachApplication方法开始分析。
1. AMS.attachApplication
public final void attachApplication(IApplicationThread thread)
synchronized (this)
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);
Binder.restoreCallingIdentity(origId);
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid)
//找到之前创建的ProcessRecord对象,之前的ProcessRecord对象还没有指向任何一个进程
ProcessRecord app;
if (pid != MY_PID && pid >= 0)
synchronized (mPidsSelfLocked)
app = mPidsSelfLocked.get(pid);
else
app = null;
//根据创建的进程来初始化这个ProcessRecord对象,使它指向新创建的进程
app.makeActive(thread, mProcessStats);
app.curAdj = app.setAdj = -100;
app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT;
app.forcingToForeground = null;
updateProcessForegroundLocked(app, false, false);
app.hasShownUi = false;
app.debugging = false;
app.cached = false;
app.killedByAm = false;
//使用generateApplicationProvidersLocked方法在PMS服务中查询所有和该进程有关的ContentProvider信息
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
……
//最终调用新建进程的bindApplication来创建并启动该ContentProvider
thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(mConfiguration), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked());
……
在该方法中调用了generateApplicationProvidersLocked方法来查询和新建进程相关的ContetProvider的信息,保存在Providers列表中。同时在该方法中,还将查询到的每一个ContentProvider的信息,封装成了一个ContentProviderRecord对象,保存了ProviderMap中。
随后调用新建进程的binderApplication方法的时候将providers信息作为参数传入了新建进程。
2. ApplicationThread.bindApplication
public final void bindApplication(String processName, ApplicationInfo appInfo,
List<ProviderInfo> providers, ComponentName instrumentationName,
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
Bundle coreSettings)
……
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providers;
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableOpenGlTrace = enableOpenGlTrace;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
sendMessage(H.BIND_APPLICATION, data);
ApplicationThread接收到AMS服务发送的请求,然后通过Handler发送BIND_APPLICATION消息给Handler来处理。Handler的handlerMessage接收到BIND_APPLICATION消息后,调用ActivityThread的handBindApplication方法来处理。
3. ActivityThread.handleBindApplication
List<ProviderInfo> providers = data.providers;
if (providers != null)
installContentProviders(app, providers);
// For process that contains content providers, we want to
// ensure that the JIT is enabled "at some point".
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
handleBindApplication方法中我们只分析和初始化ContentProvider相关的代码逻辑。首先获取到参数传递的和本进程有关的provider的信息,调用installContentProviders方法来安装并启动contentProvider组件。
4. ActivityThread.installContentProviders
private void installContentProviders(
Context context, List<ProviderInfo> providers)
final ArrayList<IActivityManager.ContentProviderHolder> results =
new ArrayList<IActivityManager.ContentProviderHolder>();
for (ProviderInfo cpi : providers)
……
//通过调用installProvider方法,生成了一个ContetProviderHolder对象
IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null)
cph.noReleaseNeeded = true;
results.add(cph);
try
//通知AMS服务来发布这些ContentProvider,这样其他进程就可以通过AMS服务来访问这些ContentProvider了
ActivityManagerNative.getDefault().publishContentProviders(
getApplicationThread(), results);
catch (RemoteException ex)
这个方法主要做了两件事。
第一.通过循环变量providerinfo信息,调用installProvider方法将provider信息安装完成并封装成了一个ContentProviderHolder类型的对象。
第二.调用AMS服务的publishContentProviders方法,将这些安装完成的Provider信息发布到AMS服务,以便其他进程访问。
那就先来看installProvider的处理过程,在来看AMS发布ContentProvider的过程。
5. ActivityThread.installProvider过程
private IActivityManager.ContentProviderHolder installProvider(Context context,
IActivityManager.ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable)
ContentProvider localProvider = null;
IContentProvider provider;
if (holder == null || holder.provider == null)
……
Context c = null;
ApplicationInfo ai = info.applicationInfo;
if (context.getPackageName().equals(ai.packageName))
c = context;
else if (mInitialApplication != null &&
mInitialApplication.getPackageName().equals(ai.packageName))
c = mInitialApplication;
……
……
try
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
//获取ContetProvider的IContentProvider赋值给provider变量
provider = localProvider.getIContentProvider();
localProvider.attachInfo(c, info);
else
……
我们首先来看这个方法的第一部分。在installContentProviders方法中调用这个方法的时候,holder参数传递的值为Null,也是因为这些ContentProvider是第一次安装。所以holde肯定为Null。所以此时满座if的条件。在If语句中,首先根据条件获取相应的Context上下文信息。
然后ClassLoader加载对应的ContentProvider类,并创建该类的对象,然后调用ContentProvider的attach方法。该方法作用是将新创建的ContentProvider和Context,ProviderInfo关联起来,最后调用该Provider的onCreate方法启动ContentProvider。这个一个ContentProvider就创建完成了,下一步就是将它保存到应用进程的中,以方便查找和管理。并发布到AMS服务中,方便其他进程调用。
获取ContetProvider的IContentProvider赋值给provider变量,IContentProvider是ContentProvider客户端和服务端通信的接口,genIcontentProvider理解为得到一个Binder类型的对象,用于ContentProvider客户端和服务端之间的通信。
IActivityManager.ContentProviderHolder retHolder;
synchronized (mProviderMap)
IBinder jBinder = provider.asBinder();
if (localProvider != null)
ComponentName cname = new ComponentName(info.packageName, info.name);
ProviderClientRecord pr = mLocalProvidersByName.get(cname);
//第一次创建ContentProvider,还没有保存,所以pr为null
if (pr != null)
provider = pr.mProvider;
else
//根据Provider创建ContentProviderHolder对象
holder = new IActivityManager.ContentProviderHolder(info);
holder.provider = provider;
holder.noReleaseNeeded = true;
//通过该方法吧Provider的信息保存到ProviderMap中
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
mLocalProviders.put(jBinder, pr);
mLocalProvidersByName.put(cname, pr);
retHolder = pr.mHolder;
else
……
return retHolder;
由于是第一次启动ContentProvider,所以该信息还没有保存,所以变量pr为空,此时根据ProviderInfo的信息和Binder类型IContentProvider对象,创建一个ContentProviderHolder对象,它里边封装了这个ContentProvider的ProviderInfo和IContentProvider信息。
方法最后返回创建的这个ContentProviderHolder的对象。
接着看看一下installProviderAuthoritiesLocked的实现
installProviderAuthoritiesLocked
private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
ContentProvider localProvider, IActivityManager.ContentProviderHolder holder)
final String auths[] = holder.info.authority.split(";");
final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);
final ProviderClientRecord pcr = new ProviderClientRecord(
auths, provider, localProvider, holder);
for (String auth : auths)
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord existing = mProviderMap.get(key);
if (existing != null)
Slog.w(TAG, "Content provider " + pcr.mHolder.info.name
+ " already published as " + auth);
else
mProviderMap.put(key, pcr);
return pcr;
根据Provider的信息创建了一个ProviderClientRecord对象,authority是一个多属性值,变量这个Provider对应的所有authority,每个authority属性为key,保存这个ProviderClientReocrd到mProviderMap描述的HashMap中。
在一个应用进程中有三个列表来保存本进程中的ContentProvider的信息。
1. ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
主要以authority为key,保存providerClientRecord信息
2. ArrayMap<IBinder, ProviderClientRecord> mLocalProviders
以通信的接口Binder对象为key保存ProviderClientRecord对象。主要保存了本进程的ContentProvider的信息
3. ArrayMap<ComponentName,ProviderClientRecord> mLocalProvidersByName
以Provider的ComponentName信息为key 保存ProviderClientRecord对象。主要保存了本进程的ContentProvider的信息
通过installProvider方法将ContentProvider的类加载到内存中来,并创建了ContentProvider的对象,调用了ContentProvider的onCreate来启动它。然后将它按照不同的存储类型分别保存不同的ContentProvider集合中。
6. AMS.publishContentProviders过程
ContentProvider本地创建完成并保存后,将它封装成立一个ContentProviderHolder对象返回,然后我们调用AMS的publishContentProviders方法,将这些Holder对象发送给AMS服务将他们发布到AMS服务中。
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers)
if (providers == null)
return;
synchronized (this)
final ProcessRecord r = getRecordForAppLocked(caller);
final int N = providers.size();
for (int i = 0; i < N; i++)
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null)
continue;
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (dst != null)
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
mProviderMap.putProviderByClass(comp, dst);
//将ContentProvider的信息按照Authority为key保存到AMS的ProviderMap集合中
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++)
mProviderMap.putProviderByName(names[j], dst);
//查询是否有进程在等待这个ContentProvider启动,如果等待列表中有,从等待列表删除
int launchingCount = mLaunchingProviders.size();
int j;
boolean wasInLaunchingProviders = false;
for (j = 0; j < launchingCount; j++)
if (mLaunchingProviders.get(j) == dst)
mLaunchingProviders.remove(j);
wasInLaunchingProviders = true;
j--;
launchingCount--;
if (wasInLaunchingProviders)
mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
synchronized (dst)
//将AMS服务中这个ConentProviderRecord指向应用进程的一个ContentProviderClientRecord.
dst.provider = src.provider;
//将AMS服务中这个ConentProviderRecord指向一个对应的应用进程
dst.proc = r;
//通知这个ContentProvider已经启动完成,结束等待
dst.notifyAll();
在前面generateApplicationProvidersLocked方法查询在该进程运行的所有ContentProvider的信息的时候,为每一个ContentProvider信息创建了一个ContentProviderRecord对象,保存到了ProviderMap集合中。那时候这些ContnetProvider还没有启动创建,只是一些ContentProvider的信息。这个方法就是要将创建并启动的ContentProvider和ContentProviderRecord关联起来。
首先将启动的ContentProvider按照authority为key保存到ProviderMap中。然后将ContentProviderRecord的provider指向应用进程中启动的ContentProviderClientRecord对象,proc指向新启动应用进程。这样ContentProvider就在AMS服务中发布完成了。发布的工作主要就是将ContentProvider的信息保存到AMS服务中去。
AMS服务保存ContentProvider的信息主要是在类ProviderMap中,它里边有两种保存的Provider信息的集合,以两种不同的方式保存了provider的信息。
1. ProviderByClass
以ComponentName为key保存了ContentProviderRecord的信息
2. ProviderByName
以authority为key保存了ContentProviderRecord的信息
本进程的ContentProvider的启动过程就分析完成了。
接下来几行代码和和不同进程间调用ContentProvider有关。我们顺便分析下。
如果是不同进程间调用ContentProvider的时候,首先会判断该ContentProvider所在的进程是否已经启动,如果有启动需要首先启动该进程,在该进程启动完成后这个ContentProvider也就启动起来了。
如果没有启动的时候,AMS就会首先启动个进程及ContentProvider,并把这个ContentProviderRecord添加到等待队列mLaunchingProviders中去,然后等他它启动完成。
此处代码就是新的进程及ContentProvider启动完成后,首先判断是否在等待进程中,如果有,就将该ContentProvider信息从等待队列中移除,并调用notifyAll来唤醒等待的工作。
调用ContentProvider的过程分析
ContentProvider的调用方法:
getContext().getContentResolver().insert(uri);
我们就分析insert方法来分析ContentProvider的调用过程。
getContext最终的实现是在ContextImpl中,那我们就从ContextImpl的getContentResolver开始分析
1. ContextImpl.getContentResolver
public ContentResolver getContentResolver()
return mContentResolver;
private final ApplicationContentResolver mContentResolver;
private static final class ApplicationContentResolver extends ContentResolver
private final ActivityThread mMainThread;
private final UserHandle mUser;
public ApplicationContentResolver(
Context context, ActivityThread mainThread, UserHandle user)
super(context);
mMainThread = Preconditions.checkNotNull(mainThread);
mUser = Preconditions.checkNotNull(user);
从代码中可以看出,ContextImpl的getContentResolver最终返回的是一个ApplicationContentResolver的对象。这个ApplicationContentResolver是ContextImpl的一个内部类,继承自ContentResolver。在ContextImpl创建的时候就创建了这个对象。
Insert方法最终就是要看ApplicationContentResolver的Insert方法的实现了。但是ApplicationContentResolver里面并没有Insert方法,所以最终还是要看ContentResolver的insert方法。
2. ContentResolver.insert
public final @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues values)
Preconditions.checkNotNull(url, "url");
//获取调用ContentProvider的接口
IContentProvider provider = acquireProvider(url);
try
long startTime = SystemClock.uptimeMillis();
//调用contentProvider的insert方法
Uri createdRow = provider.insert(mPackageName, url, values);
long durationMillis = SystemClock.uptimeMillis() - startTime;
return createdRow;
catch (RemoteException e)
……
finally
releaseProvider(provider);
这个方法主要做了两个工作:
第一. 通过acquireProvider方法获取ContentProvider的通信接口,客户端通过该通信接口可以调用服务端的ContentProvider的方法。
第二. 通过获取的通信接口,调用ContentProvider的insert方法,插入数据。
ContentProvider的客户端和服务端通信主要通过Binder间进程通信实现和AMS服务关系不是太大,此处不再详细分析。主要分析获取ContentProvider通信接口的过程。
调用ContentResolver的acquireProvider方法,该方法是一个abstrict 方法。在ApplicationContentResolver中实现了该方法。
3. ApplicationContentResolver.acquireProvider
protected IContentProvider acquireProvider(Context context, String auth)
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
mMainThread就是ActivityThread,此处直接调用ActivityThread的acquireProvider方法。
4. ActivityThread. acquireProvider
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable)
//首先查看是否已经保存了该ContentProvider的通信接口,已经保存的话直接返回
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null)
return provider;
//未保存的话,去AMS服务查询
IActivityManager.ContentProviderHolder holder = null;
try
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
catch (RemoteException ex)
if (holder == null)
Slog.e(TAG, "Failed to find provider info for " + auth);
return null;
//根据查询的结果,保存到本地进程中
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
1. 通过acquireExistingProvider方法检查本地进程是否已经保存了该ContentProvider的通信接口,如果已经保存的话直接返回,不再需要去AMS服务查询并在本地保存安装。
2. 如果本地没有保存,请求AMS服务的getContentProvider方法,去获取该ContentProvider的信息。
3. 根据从AMS服务获取的信息,将该ContentPrivider保存在本地
5. ActivityThread. acquireExistingProvider
public final IContentProvider acquireExistingProvider(
Context c, String auth, int userId, boolean stable)
synchronized (mProviderMap)
//在本进程的ProviderMap中查询是否包含请求的ContentProvider信息
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord pr = mProviderMap.get(key);
if (pr == null)
return null;
//如果有的话,直接返回并对该ContentProvider引用加1
IContentProvider provider = pr.mProvider;
IBinder jBinder = provider.asBinder();
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null)
incProviderRefLocked(prc, stable);
return provider;
这个方法的实现逻辑比较简单,直接根据要请求ContentProvider的Uri从ProviderMap的集合中查询,如果已经存在直接返回该Provider的通信接口IContentProvider,并对它的引用加1。
一般情况下调用本进程的ContentProvider都是存在的,因为在进程启动的时候这些ContentProvider就已进启动完成,并保存到相关的集合中去了。
如果不存在,那就应该是第一次调用其他进程的ContentProvider,接着往下分析。
6. AMS.getContentProvider
getContentProvider直接调用getContentProviderImpl方法来实现
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId)
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
synchronized(this)
long startTime = SystemClock.elapsedRealtime();
ProcessRecord r = null;
if (caller != null)
r = getRecordForAppLocked(caller);
//首先检查该ContentProvider是否已经发布
cpr = mProviderMap.getProviderByName(name, userId);
boolean providerRunning = cpr != null;
//已经发布
if (providerRunning)
cpi = cpr.info;
//检查是否允许该ContentProvider在多个进程中运行,允许的话直接返回Holder为Null
if (r != null && cpr.canRunHere(r))
ContentProviderHolder holder = cpr.newHolder(null);
holder.provider = null;
return holder;
boolean singleton;
//还未发布
if (!providerRunning)
……
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
cpr = mProviderMap.getProviderByClass(comp, userId);
//还未发布的情况下cpr还没有保存,查询ContentProvider信息,并封装成一个ContentProviderReocrd对象
final boolean firstClass = cpr == null;
if (firstClass)
final long ident = Binder.clearCallingIdentity();
try
checkTime(startTime, "getContentProviderImpl: before getApplicationInfo");
ApplicationInfo ai =
AppGlobals.getPackageManager().
getApplicationInfo(
cpi.applicationInfo.packageName,
STOCK_PM_FLAGS, userId);
checkTime(startTime, "getContentProviderImpl: after getApplicationInfo");
if (ai == null)
Slog.w(TAG, "No package info for content provider "
+ cpi.name);
return null;
ai = getAppInfoForUser(ai, userId);
cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
if (r != null && cpr.canRunHere(r))
//检查是否允许该ContentProvider在多个进程中运行,允许的话直接返回Holder为Null
return cpr.newHolder(null);
不允许的话,启动ContentProvider及其所在的进程,把这个ContentProviderRecord添加到等待队列中去
final int N = mLaunchingProviders.size();
int i;
for (i = 0; i < N; i++)
if (mLaunchingProviders.get(i) == cpr)
break;
if (i >= N)
final long origId = Binder.clearCallingIdentity();
try
//判断该进程是否已经启动,如果已经启动的话直接调用scheduleInstallProvider来启动该ContentProvider
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null)
if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER,
"Installing in existing process " + proc);
if (!proc.pubProviders.containsKey(cpi.name))
checkTime(startTime, "getContentProviderImpl: scheduling install");
proc.pubProviders.put(cpi.name, cpr);
try
proc.thread.scheduleInstallProvider(cpi);
catch (RemoteException e)
else
//进程没有启动,则直接启动进程
checkTime(startTime, "getContentProviderImpl: before start process");
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
checkTime(startTime, "getContentProviderImpl: after start process");
if (proc == null)
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
+ name + ": process is bad");
return null;
cpr.launchingApp = proc;
mLaunchingProviders.add(cpr);
finally
Binder.restoreCallingIdentity(origId);
//将该ContentProviderRecord保存到mProviderMap中
if (firstClass)
mProviderMap.putProviderByClass(comp, cpr);
mProviderMap.putProviderByName(name, cpr);
conn = incProviderCountLocked(r, cpr, token, stable);
if (conn != null)
conn.waiting = true;
checkTime(startTime, "getContentProviderImpl: done!");
//一直等到这个Provider启动完成之后唤醒
synchronized (cpr)
……
cpr.wait();
……
return cpr != null ? cpr.newHolder(conn) : null;
这个方法的逻辑有点复杂。首先从mProviderMap中查询这个ContentProvider是否存在。
1. 如果存在,表示已经发布
a. 首先检查该Provider是否允许在多个进程中运行。如果运行多个进程中都有实例的话,直接返回ContentProviderHolder为null
一般情况下,ContentProvider的服务端都是运行在进程应用所在的进程中,但是如果ContentProvider的属性multiProcess为true的话表示,该ContentProvider可以在多个进程中都有实例。
b. 不允许在多个进程中都有实例,直接返回已经发布的ContentProvider的Holder信息
2. 如果不存在,表示该ContentProvider还没有发布
如果还没有发布,则首先去查询该ContentProvider的ProviderInfo信息。然后根据ProviderInfo,再去查询ContentProviderRecord是否在ProviderMap中存在。第一次调用的话ContentProviderRecord肯定为null.然后去PMS服务中查询ContentProvider信息,创建一个ContentProviderRecord对象。
a. 判断是否允许多个进程中都有实例,允许的话直接返回Holder为Null
b. 不允许的允许多个进程中都有实例,需要启动该ContentProvider及其所在的进程。
a) 该进程已经启动
直接通知该进程安装并启动ContentProvider,并将该ContentProviderRecord保存到ProviderMap中。然后添加到mLaunchingProviders等待队列,调用wait方法等待该 ContentProvider启动完成。
b) 该进程及ContentProvider都还未创建启动
启动该ContentProvider所在的进程,并启动ContentProvider.此处逻辑和前面ContentProvider启动过程一致。将该ContentProviderRecord保存到ProviderMap中。然后添加到mLaunchingProviders等待队列,调用wait方法等待该ContentProvider启动完成。
当ContentProvider启动后,唤醒此处等待,然后返回该ContentProviderHolder信息
以上是关于ContentProvider的启动流程分析的主要内容,如果未能解决你的问题,请参考以下文章
Android 源码分析 ContentProvider 启动
ContentProvider启动过程and多进程调用全过程源码详解