ContentProvider的启动过程

基于Android U

ContentProvider,即内容提供者,主要用于进程内和进程间的数据共享。

当ContentProvider所在进程启动时,ContentProvider会同时启动并被发布到AMS中。需要注意的是,这时ContentProvider的onCreate()先于Application的onCreate()执行。

外界无法直接访问ContentProvider,只能通过AMS根据Uri获取对应ContentProvider的Binder接口IContentProvider,然后再通过IContentProvider来访问ContentProvider中的数据源。访问ContentProvider需要通过ContentResolver,ContentResolver是一个抽象类,通过Context的**getContentResolver()**方法获取。真正实现是ApplicationContentResolver(ContextImpl的内部类)。当ContentProvider所在的进程未启动时,第一次访问ContentResolver的方法时,就会启动该进程和ContentProvider。

通过增删改查四个方法的任何一个都可以触发ContentProvider的启动。

query方法到AMS的调用过程

1
2
3
4
frameworks/base/core/java/android/content/ContextWrapper.java
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}

mBase指的是ContextImpl。

1
2
3
4
frameworks/base/core/java/android/app/ContextImpl.java
public ContentResolver getContentResolver() {
return mContentResolver;
}

getContentResolver()方法中返回了ApplicationContentResolver类型的mContentResolver对象,ApplicationContentResolver是ContextImpl中的静态内部类,继承自ContentResolver,它在ContextImpl的构造方法中被创建,这说明当我们调用ContentResolver的insert、query、update等方法时就会启动ContentProvider。这里以query()方法来举例,query()方法在ApplicationContentResolver的父类ContentResolver中实现,有3个重载方法,最终会调用如下的query()方法:

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
frameworks/base/core/java/android/content/ContentResolver.java
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal) {
...
IContentProvider unstableProvider = acquireUnstableProvider(uri); // 1
...
try {
...
try {
qCursor = unstableProvider.query(mContext.getAttributionSource(), uri, projection,
queryArgs, remoteCancellationSignal); // 2
} catch (DeadObjectException e) {
...
}
if (qCursor == null) {
return null;
}
...
} catch (RemoteException e) {
...
} finally {
...
}
}

注释1处通过acquireUnstableProvider()方法返回IContentProvider类型的unstableProvider对象,注释2处调用unstableProvider的query()方法。IContentProvider是ContentProvider在本地的代理,具体的实现为ContentProvider。

1
2
3
4
5
6
7
8
9
10
11
frameworks/base/core/java/android/content/ContentResolver.java
public final IContentProvider acquireUnstableProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) { // 1
return null;
}
String auth = uri.getAuthority();
if (auth != null) {
return acquireUnstableProvider(mContext, uri.getAuthority()); // 2
}
return null;
}

注释1处检查uri的scheme是否等于SCHEME_CONTENT(值为”content”),如果不是则返回null。

注释2处调用了acquireUnstableProvider()方法,这是个抽象方法,它在ContentResolver的子类ApplicationContentResolver中实现,ApplicationContentResolver是ContextImpl的静态内部类。

1
2
3
4
5
6
7
8
frameworks/base/core/java/android/app/ContextImpl.java
private static final class ApplicationContentResolver extends ContentResolver {
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
}

在acquireUnstableProvider()方法中返回了ActivityThread类型的mMainThread对象的acquireProvider()方法。

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
frameworks/base/core/java/android/app/ActivityThread.java
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable); // 1
if (provider != null) {
return provider;
}

ContentProviderHolder holder = null;
final ProviderKey key = getGetProviderKey(auth, userId);
try {
synchronized (key) {
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), c.getOpPackageName(), auth, userId, stable); // 2
...
}
} catch (RemoteException ex) {
...
} catch (InterruptedException e) {
...
} finally {
...
}
...

// Install provider will increment the reference count for us, and break
// any ties in the race.
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable); // 3
return holder.provider;
}

注释1处的acquireExistingProvider()方法内部会检查ActivityThread的全局变量mProviderMap中是否有目标ContentProvider存在,有则返回,没有就会在注释2处调用IActivityManager的getContentProvider()方法,最终会调用AMS的getContentProvider()方法。注释3处的installProvider()方法用来安装ContentProvider,并将注释2处返回的ContentProvider相关的数据存储在mProviderMap中,起到缓存的作用,这样使用相同的ContentProvider时,就不需要每次都调用AMS的getContentProvider()方法了。

1
2
3
4
5
6
7
8
9
10
11
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String callingPackage, String name, int userId,
boolean stable) {
...
try {
return mCpHelper.getContentProvider(caller, callingPackage, name, userId, stable); // 1
} finally {
...
}
}

调用的ContentProviderHelper的getContentProvider()。

1
2
3
4
5
6
7
frameworks/base/services/core/java/com/android/server/am/ContentProviderHelper.java
ContentProviderHolder getContentProvider(IApplicationThread caller, String callingPackage,
String name, int userId, boolean stable) {
...
return getContentProviderImpl(caller, name, null, callingUid, callingPackage,
null, stable, userId);
}

getContentProvider()方法返回了getContentProviderImpl()方法。

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
frameworks/base/services/core/java/com/android/server/am/ContentProviderHelper.java
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, int callingUid, String callingPackage, String callingTag,
boolean stable, int userId) {
...
synchronized (mService) {
...
if (!providerRunning) {
...
// If the provider is not already being launched, then get it started.
if (i >= numLaunchingProviders) {
...
try {
...
ProcessRecord proc = mService.getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid); // 1
IApplicationThread thread;
if (proc != null && (thread = proc.getThread()) != null
&& !proc.isKilled()) {
...
final ProcessProviderRecord pr = proc.mProviders;
if (!pr.hasProvider(cpi.name)) {
checkTime(startTime, "getContentProviderImpl: scheduling install");
pr.installProvider(cpi.name, cpr);
try {
thread.scheduleInstallProvider(cpi); // 2
} catch (RemoteException e) {
}
}
...
} else {
...
proc = mService.startProcessLocked(
cpi.processName, cpr.appInfo, false, 0,
new HostingRecord(HostingRecord.HOSTING_TYPE_CONTENT_PROVIDER,
new ComponentName(
cpi.applicationInfo.packageName, cpi.name)),
Process.ZYGOTE_POLICY_FLAG_EMPTY, false, false); // 3
...
}
...
} finally {
...
}
}
...
}
...
}
...
}

注释1处通过getProcessRecordLocked()方法来获取目标ContentProvider的应用程序进程信息,这些信息用ProcessRecord类型的proc来表示,如果该应用程序进程已经启动就会调用注释2处的代码,否则就会调用注释3处的startProcessLocked()来启动进程。这里假设ContentProvider的应用程序进程还没有启动,应用程序进程启动最终会调用ActivityThread的main()方法。

1
2
3
4
5
6
7
8
9
10
11
12
frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
...
Looper.prepareMainLooper(); // 1
...
ActivityThread thread = new ActivityThread(); // 2
thread.attach(false, startSeq); // 3
...
Looper.loop(); // 4

throw new RuntimeException("Main thread loop unexpectedly exited");
}

注释1处通过prepareMainLooper()方法在ThreadLocal中获取Looper,并在注释4处开启消息循环。注释2处创建了ActivityThread,紧接着调用了它的attach()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
frameworks/base/core/java/android/app/ActivityThread.java
private void attach(boolean system, long startSeq) {
...
if (!system) {
...
final IActivityManager mgr = ActivityManager.getService(); // 1
try {
mgr.attachApplication(mAppThread, startSeq); // 2
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
...
} else {
...
}
...
}

注释1处得到IActivityManager,注释2处调用IActivityManager的attachApplication()方法,并将ApplicationThread类型的mAppThread对象传进去,最终调用的是AMS的attachApplication()方法。

AMS启动ContentProvider的过程

1
2
3
4
5
6
7
8
9
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public final void attachApplication(IApplicationThread thread, long startSeq) {
...
synchronized (this) {
...
attachApplicationLocked(thread, callingPid, callingUid, startSeq);
...
}
}

在attachApplicationLocked()方法中又调用了attachApplicationLocked()方法。

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
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
private void attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
...
try {
...
final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
...
if (app.getIsolatedEntryPoint() != null) {
// This is an isolated process which should just call an entry point instead of
// being bound to an application.
thread.runIsolatedEntryPoint(
app.getIsolatedEntryPoint(), app.getIsolatedEntryPointArgs());
} else if (instr2 != null) {
thread.bindApplication(processName, appInfo,
app.sdkSandboxClientAppVolumeUuid, app.sdkSandboxClientAppPackage,
providerList,
instr2.mClass,
profilerInfo, instr2.mArguments,
instr2.mWatcher,
instr2.mUiAutomationConnection, 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());
} else {
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());
}
...
} catch (Exception e) {
...
}
}

在attachApplicationLocked()中调用了thread的bindApplication()方法,thread是IApplicationThread类型的,这里和IActivityManager一样采用了AIDL,ApplicationThread是ActivityThread的内部类。

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
53
frameworks/base/core/java/android/app/ActivityThread$ApplicationThread
private class ApplicationThread extends IApplicationThread.Stub {
public final void bindApplication(String processName, ApplicationInfo appInfo,
String sdkSandboxClientAppVolumeUuid, String sdkSandboxClientAppPackage,
ProviderInfoList providerList, ComponentName instrumentationName,
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
String buildSerial, AutofillOptions autofillOptions,
ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges,
SharedMemory serializedSystemFontMap,
long startRequestedElapsedTime, long startRequestedUptime) {
if (services != null) {
...
// Setup the service cache in the ServiceManager
ServiceManager.initServiceCache(services);
}

setCoreSettings(coreSettings);

AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.sdkSandboxClientAppVolumeUuid = sdkSandboxClientAppVolumeUuid;
data.sdkSandboxClientAppPackage = sdkSandboxClientAppPackage;
data.providers = providerList.getList();
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableBinderTracking = enableBinderTracking;
data.trackAllocation = trackAllocation;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
data.buildSerial = buildSerial;
data.autofillOptions = autofillOptions;
data.contentCaptureOptions = contentCaptureOptions;
data.disabledCompatChanges = disabledCompatChanges;
data.mSerializedSystemFontMap = serializedSystemFontMap;
data.startRequestedElapsedTime = startRequestedElapsedTime;
data.startRequestedUptime = startRequestedUptime;
updateCompatOverrideScale(compatInfo);
CompatibilityInfo.applyOverrideScaleIfNeeded(config);
sendMessage(H.BIND_APPLICATION, data);
}
}

在bindApplication()方法中最后调用sendMessage()方法向H发送BIND_APPLICATION类型消息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
frameworks/base/core/java/android/app/ActivityThread$H
class H extends Handler {
public void handleMessage(Message msg) {
...
switch (msg.what) {
case BIND_APPLICATION:
...
handleBindApplication(data);
...
break;
...
}
...
}
}

接下来执行handleBindApplication()。

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
frameworks/base/core/java/android/app/ActivityThread.java
private void handleBindApplication(AppBindData data) {
...
Application app;
...
try {
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
app = data.info.makeApplicationInner(data.restrictedBackupMode, null); // 1
...
mInitialApplication = app;
...
// don't bring up providers in restricted mode; they may depend on the
// app's custom Application class
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers); // 2
}
}
...
try {
mInstrumentation.callApplicationOnCreate(app); // 3
} catch (Exception e) {
...
}
} finally {
...
}
...
}

注释1处创建Application,注释2处在非受限模式下启动ContentProvider,注释3处执行Application的onCreate()方法。

ContentProvider的启动时机是在Application创建后,Application#onCreate()调用前。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
frameworks/base/core/java/android/app/ActivityThread.java
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();

for (ProviderInfo cpi : providers) { // 1
...
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/); // 2
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}

try {
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results); // 3
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}

注释1处遍历当前应用程序进程的ProviderInfo列表,得到每个ContentProvider的ProviderInfo(存储ContentProvider的信息),并在注释2处调用installProvider()方法来启动这些ContentProvider。注释3处通过AMS的publishContentProviders()方法将这些ContentProvider存储在AMS的mProviderMap中,这个mProviderMap在前面提到过,起到缓存的作用,防止每次使用相同的ContentProvider时都会调用AMS的getContentProvider()方法。

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
frameworks/base/core/java/android/app/ActivityThread.java
private ContentProviderHolder installProvider(Context context,
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;
// 1
if (context.getPackageName().equals(ai.packageName)) {
c = context;
} else if (mInitialApplication != null &&
mInitialApplication.getPackageName().equals(ai.packageName)) {
c = mInitialApplication;
} else {
try {
c = context.createPackageContext(ai.packageName,
Context.CONTEXT_INCLUDE_CODE);
} catch (PackageManager.NameNotFoundException e) {
// Ignore
}
}
...
try {
final java.lang.ClassLoader cl = c.getClassLoader();
LoadedApk packageInfo = peekPackageInfo(ai.packageName, true); // 2
if (packageInfo == null) {
// System startup case.
packageInfo = getSystemContext().mPackageInfo;
}
localProvider = packageInfo.getAppFactory()
.instantiateProvider(cl, info.name); // 3
provider = localProvider.getIContentProvider();

...
localProvider.attachInfo(c, info); // 4
} catch (java.lang.Exception e) {
...
}
} else {
...
}
...
}

注释1处首先获取Context,一般情况下就是Application;

注释2处获取应用信息;

注释3处通过AppComponentFactory实例化ContentProvider;

注释4处初始化ContentProvider,调用其onCreate()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
frameworks/base/core/java/android/content/ContentProvider.java
public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);
}

private void attachInfo(Context context, ProviderInfo info, boolean testing) {
...
if (mContext == null) {
mContext = context;
...
ContentProvider.this.onCreate(); // 1
}
}

注释1处在attachInfo()方法中调用了onCreate()方法,它是一个抽象方法,这样ContentProvider就启动完毕。


ContentProvider的启动过程
https://citrus-maxima.github.io/2024/03/10/ContentProvider的启动过程/
作者
柚子树
发布于
2024年3月10日
许可协议