ContentProvider的启动过程

前言

ContentProvider是一种内容共享型组件,它通过Binder向其他组件或应用提供数据。

当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方法分析:

源码分析

ContentResolver#query

在ContentResolver的query方法中,需要先要获取IContentProvider对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal) {

IContentProvider unstableProvider = acquireUnstableProvider(uri);

//...

try {
//...

stableProvider = acquireProvider(uri);

//...
}
}

acquireUnstableProvider方法最终也是调用acquireProvider方法,所以接下来看ApplicationContentResolver的acquireProvider方法:

ApplicationContentResolver#acquireProvider

1
2
3
4
5
6
7
@Override
protected IContentProvider acquireProvider(Context context, String auth) {
//mMainThread是ActivityThread对象
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}

ApplicationContentResolver交给ActivityThread来处理,继续看ActivityThread的acquireProvider方法:

ActivityThread#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
  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;
}

ContentProviderHolder holder = null;
try {
//发送进程间请求,让AMS启动目标ContentProvider
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
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;
}

ActivityThread会先查找是否已经存在目标ContentProvider,如果存在就直接返回已存在的ContentProvider。否则通知AMS启动目标ContentProvider,最后通过installProvider方法修改引用数量。

在AMS中,首先启动ContentProvider所在的进程,然后再启动ContentProvider。新进程启动后,其入口方法为ActivityThread的main方法:

ActivityThread#main

1
2
3
4
5
6
7
8
9
 public static void main(String[] args) {
//...

//创建ActivityThread实例,并调用attach方法进行一系列初始化
ActivityThread thread = new ActivityThread();
thread.attach(false);

//...
}

在该方法中会创建ActivityThread实例,并调用其attach方法进行一系列初始化。

ActivityThread#attach

1
2
3
4
5
6
7
8
9
10
11
12
13
private void attach(boolean system) {
//...

final IActivityManager mgr = ActivityManager.getService();
try {
//mAppThread是一个ApplicationThread对象
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}

//...
}

在attach中,会通过AMS的attachApplication方法将ApplicationThread对象传递给AMS

ActivityManagerService#attachApplication

1
2
3
4
5
6
7
8
9
@Override
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);
Binder.restoreCallingIdentity(origId);
}
}

继续调用attachApplicationLocked方法

ActivityManagerService#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
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {

//...

if (app.instr != null) {
thread.bindApplication(processName, appInfo, providers,
app.instr.mClass,
profilerInfo, app.instr.mArguments,
app.instr.mWatcher,
app.instr.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial);
} else {
thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial);
}

//...
}

又调用了ApplicationThread的bindApplication方法

ActivityThread.ApplicationThread#bindApplication

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public final void bindApplication(String processName, ApplicationInfo appInfo,
List<ProviderInfo> providers, 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) {
//...

AppBindData data = new AppBindData();
//...

sendMessage(H.BIND_APPLICATION, data);
}

该方法发送一条消息给mH,mH对这条消息的处理是:

1
2
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);

继续看ActivityThread的handleBindApplication方法:

ActivityThread#handleBindApplication

1
2
3
4
5
6
7
8
9
private void handleBindApplication(AppBindData data) {
//...

if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);
//...
}
//...
}

在该方法中,通过调用installContentProviders方法,来启动该进程的ContentProvider。

ActivityThread#installContentProviders

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();

for (ProviderInfo cpi : providers) {
//...

//启动ContentProvider
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}

try {
//AMS把启动的ContentProvider存储在ProviderMap中
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}

在该方法中,遍历provider集合,启动每个ContentProvider,最后把启动的ContentProvider存储在AMS的ProviderMap中。下面看一下启动ContentProvider的installProvider方法:

ActivityThread#installProvider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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) {
//...

try {
final java.lang.ClassLoader cl = c.getClassLoader();
//创建ContentProvider,并获得它的IContentProvider实例
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();

//...

localProvider.attachInfo(c, info);
}
//...
}

//...
}

在该方法中,先创建完ContentProvider,并获得它的IContentProvider实例。然后,通过ContentProvider的attachInfo方法来调用它的onCreate方法。

至此,ContentProvider启动完成。

小结

ContentResolver调用增删改查的任意一个方法都可以触发ContentProvider的启动,要访问ContentProvider需先获得ContentProvider的IContentProvider对象,ContentResolver将这个任务交给ActivityThread。ActivityThread会先查找是否已经存在目标ContentProvider,如果存在就直接返回已存在的ContentProvider。否则通知AMS启动目标ContentProvider。

在AMS中,首先启动ContentProvider所在的进程,新进程启动后,其入口方法为ActivityThread的main方法,ActivityThread会将ApplicationThread对象传递给AMS,AMS之后又会通过该ApplicationThread对象通知ActivityThread启动该进程的ContentProvider,ActivityThread会创建每个ContentProvider并调用其onCreate方法,最后把启动的ContentProvider存储在AMS的ProviderMap中。

参考

  • 《Android 开发艺术探索》
-------------    本文到此结束  感谢您的阅读    -------------
0%