ContentProvider是一种内容共享型组件,实际上它是通过Binder向其它应用提供数据。当ContentProvider所在的进程启动时,ContentProvider会同时启动并被发布到AMS中,需要特别注意的是ContentProvider的onCreate方法要早于Application的onCreate方法执行。
废话不多说先看源码,As we all known,每个进程的入口都是ActivityThread.main
public static void main(String[] args) {
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
Security.addProvider(new AndroidKeyStoreProvider());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
主要看attach方法
private void attach(boolean system) {
...
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
...
}
通过前面的分析,这里一眼就可以看出IPC通信调用AMS的同名方法,接着往下看ActivityManagerService.java
@Override
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);
Binder.restoreCallingIdentity(origId);
}
}
AMS.attachApplicationLocked这个方法也是很长,这里截取最关键的
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
...
ProfilerInfo profilerInfo = profileFile == null ? null
: new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop);
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(),
mCoreSettingsObserver.getCoreSettingsLocked());
updateLruProcessLocked(app, false, null);
...
}
1.创建ActivityThread实例,并创建消息队列
2.ActivityThread的attach方法远程调用AMS的attachApplication方法,并将ApplicationThread对象传递给AMS。
3.AMS的attachApplication方法中会调用ApplicationThread的bingApplication(IPC调用)进而通过H对象切换到ActivityThread中执行handleBindApplication方法
这个方法有200多行,这里截取了最后比较关键的部分
private void handleBindApplication(AppBindData data) {
...
try {
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
// don't bring up providers in restricted mode; they may depend on the
// app's custom Application class
if (!data.restrictedBackupMode) {
List 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);
}
}
// Do this after providers, since instrumentation tests generally start their
// test thread at this point, and we don't want that racing.
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
throw new RuntimeException(
"Exception thrown in onCreate() of "
+ data.instrumentationName + ": " + e.toString(), e);
}
try {
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
} finally {
StrictMode.setThreadPolicy(savedPolicy);
}
}
其中依次会调用到
Application app = data.info.makeApplication(data.restrictedBackupMode, null);和installContentProviders(app, providers);方法,前者就是创建Application对象,而后者则是创建ContentProvider,创建ContentProvider之后则调用Application的onCreate方法。
private void installContentProviders(
Context context, List providers) {
final ArrayList results =
new ArrayList();
for (ProviderInfo cpi : providers) {
if (DEBUG_PROVIDER) {
StringBuilder buf = new StringBuilder(128);
buf.append("Pub ");
buf.append(cpi.authority);
buf.append(": ");
buf.append(cpi.name);
Log.i(TAG, buf.toString());
}
IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
ActivityManagerNative.getDefault().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
}
}
创建好后通过IPC通信,发布到AMS中,这里还没完接着往下挖,ActivityThread.installProvider
private IActivityManager.ContentProviderHolder installProvider(Context context,
IActivityManager.ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
...
try {
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();
if (provider == null) {
Slog.e(TAG, "Failed to instantiate class " +
info.name + " from sourceDir " +
info.applicationInfo.sourceDir);
return null;
}
if (DEBUG_PROVIDER) Slog.v(
TAG, "Instantiating local provider " + info.name);
// XXX Need to create the correct context for this provider.
localProvider.attachInfo(c, info);
}
...
}
仿佛看到曙光了,ContentProvider.attachInfo
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
/*
* We may be using AsyncTask from binder threads. Make it init here
* so its static handler is on the main thread.
*/
AsyncTask.init();
mNoPerms = testing;
/*
* Only allow it to be set once, so after the content service gives
* this to us clients can't change it.
*/
if (mContext == null) {
mContext = context;
if (context != null) {
mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
Context.APP_OPS_SERVICE);
}
mMyUid = Process.myUid();
if (info != null) {
setReadPermission(info.readPermission);
setWritePermission(info.writePermission);
setPathPermissions(info.pathPermissions);
mExported = info.exported;
mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
setAuthorities(info.authority);
}
ContentProvider.this.onCreate();
}
}
最后一行代码验证了我们前面的结论,
虽然Application创建在前,但ContentProvider的onCreate方法调用却在Application的onCreate之前.
当ContentProvider所在的进程启动的时候,它会同时被启动并被发布到AMS中,这个时候它的onCreate要先去Application的onCreate执行。
这部分的逻辑,我在书上看到的一幅图我觉得无可挑剔,直接拍下来放在博客上
ContentProvider的启动过程:
1.当一个应用启动时,入口方法是ActivityThread的main方法,其中创建ActivityThread的实例并创建主线程的消息队列;
2.ActivityThread的attach方法中会远程调用ActivityManagerService的attachApplication,并将ApplicationThread提供给AMS,ApplicationThread主要用于ActivityThread和AMS之间的通信;
3.ActivityManagerService的attachApplication会调用ApplicationThread的bindApplication方法,这个方法会通过H切换到ActivityThread中去执行,即调用handleBindApplication方法;
4.handleBindApplication方法会创建Application对象并加载ContentProvider,注意是先加载ContentProvider,然后调用Application的onCreate方法。
(3)ContentProvider的android:multiprocess属性决定它是否是单实例,默认值是false,也就是默认是单实例。当设置为true时,每个调用者的进程中都存在一个ContentProvider对象。实际使用中并未发现这样的使用场景,所以一般我们可以认为其为单实例的。
当调用ContentProvider的insert、delete、update、query方法中的任何一个时,如果ContentProvider所在的进程没有启动的话,那么就会触发ContentProvider的创建,并伴随着ContentProvider所在进程的启动。
这里直接从query方法顺藤摸瓜,我们的一般使用就是在Context中使用getContentResolver.query方法来获取数据
这里的Context.getContentResolver获取的是ApplicationContentResolver对象,它是ContextImpl的内部类,继承了ContentResolver并实现了其抽象方法。
代码在ContextImpl.java中
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);
}
@Override
protected IContentProvider acquireProvider(Context context, String auth) {
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}
@Override
protected IContentProvider acquireExistingProvider(Context context, String auth) {
return mMainThread.acquireExistingProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}
@Override
public boolean releaseProvider(IContentProvider provider) {
return mMainThread.releaseProvider(provider, true);
}
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
@Override
public boolean releaseUnstableProvider(IContentProvider icp) {
return mMainThread.releaseProvider(icp, false);
}
@Override
public void unstableProviderDied(IContentProvider icp) {
mMainThread.handleUnstableProviderDied(icp.asBinder(), true);
}
@Override
public void appNotRespondingViaProvider(IContentProvider icp) {
mMainThread.appNotRespondingViaProvider(icp.asBinder());
}
/** @hide */
protected int resolveUserIdFromAuthority(String auth) {
return ContentProvider.getUserIdFromAuthority(auth, mUser.getIdentifier());
}
}
看完了
getContentResolver.query的类,接着看方法的实现,方法实现在ApplicationContentResolver父类ContentResolver中
public final Cursor query(final Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
CancellationSignal cancellationSignal) {
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
long startTime = SystemClock.uptimeMillis();
ICancellationSignal remoteCancellationSignal = null;
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
remoteCancellationSignal = unstableProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
try {
qCursor = unstableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
}
...
...
首先会获取到一个IContentProvider对象,IContentProvider unstableProvider = acquireUnstableProvider(uri);其本质是通过调用了
ApplicationContentResolver类自身的acquireProvider来获取的,
通过上面ApplicationContentResolver类的定义可以看出直接调用了ActivityThread的acquireProvider
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
// There is a possible race here. Another thread may try to acquire
// the same provider at the same time. When this happens, we want to ensure
// that the first one wins.
// Note that we cannot hold the lock while acquiring and installing the
// provider since it might take a long time to run and it could also potentially
// be re-entrant in the case where the provider is in the same process.
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;
}
// 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);
return holder.provider;
}
以上代码首先会在ActivityThread中查找是否已经存在目标ContentProvider,如果有则直接返回。成员变量mProviderMap就是用来存储ContentProvider的
final ArrayMap mProviderMap
= new ArrayMap();
当然不存在目标ContentProvider,则通过IPC调用给AMS,让AMS来启动目标ContentProvider进程并启动ContentProvider,再通过installProvider来修改引用计数。
ActivityManagerNative.getDefault().getContentProvider( getApplicationThread(), auth, userId, stable);
接下来重点关注,AMS怎样启动ContentProvider所在进程的,沿着IPC调用往下挖 AMS.getContentProvider
@Override
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String name, int userId, boolean stable) {
enforceNotIsolatedCaller("getContentProvider");
if (caller == null) {
String msg = "null IApplicationThread when getting content provider "
+ name;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
// The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
// with cross-user grant.
return getContentProviderImpl(caller, name, null, stable, userId);
}
getContentProviderImpl也是一个长函数,截取其中重要的部分
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
...
if (!providerRunning) {
...
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null) {
if (DEBUG_PROVIDER) {
Slog.d(TAG, "Installing in existing process " + proc);
}
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);
...
...
}
启动ContentProvider所在进程的就是AMS.startProcessLocked,其内部主要是通过Process的start方法来完成新进程的启动的,对这一部分好奇的读者可以移步到老罗的博客:Android应用程序进程启动过程的源代码分析。新进程启动后入口就是ActivityThread.main方法,这跟前面ContentProvider启动部分一致了。
就此总结一下前面ActivityThread.handleBindApplication方法
1.创建ContextImpl和Instrumentation
2.创建Application对象
3.启动ContentProvider所在进程并调用OnCreate方法
4.调用Application的OnCreate方法
到这里ContentProvider所在进程已经启动成功,其他应用也就可以通过它提供的接口方法来访问它了。需要注意的是其他应用拿到的并不是ContentProvider对象,而是IContentProvider对象,了解过AIDL的童鞋都知道这里的IContentProvider就是ContentProvider的Binder对象,IContentProvider的具体实现是ContentProviderNative和ContentProvider.Transport,而其中ContentProvider.Transport继承了ContentProviderNative。我们可以从AMN.getDefault()可以找到类似的设计。
其他应用通过AMS获取到ContentProvider的Binder引用IContentProvider,接着通过IPC调用执行ContentProvider所在进程方法
ContentProvider.Transport
class Transport extends ContentProviderNative {
AppOpsManager mAppOpsManager = null;
int mReadOp = AppOpsManager.OP_NONE;
int mWriteOp = AppOpsManager.OP_NONE;
ContentProvider getContentProvider() {
return ContentProvider.this;
}
@Override
public String getProviderName() {
return getContentProvider().getClass().getName();
}
@Override
public Cursor query(String callingPkg, Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
ICancellationSignal cancellationSignal) {
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
CancellationSignal.fromTransport(cancellationSignal));
}
final String original = setCallingPackage(callingPkg);
try {
return ContentProvider.this.query(
uri, projection, selection, selectionArgs, sortOrder,
CancellationSignal.fromTransport(cancellationSignal));
} finally {
setCallingPackage(original);
}
}
可以清楚的看到这ContentProvider调用了自己的query方法,结果再通过Binder返回给调用进程。至此其他应用调用ContentProvider提供方法的过程分析完毕。其他update、insert、delete流程类似。