一、前言
终于到了ContentProvider,四大组件的尾声了。一般情况(至少我是这样),用到ContentProvider的机会不多,他是Android专门用来跨进程通信使用的,底层也是binder实现。怎么实现的呢?往下看吧。
一般我们使用的场景是这样的:
Uri uri = Uri.parse("com.ooo.xxx");
getContentResolver().query(uri,null,null,null,null);
直接通过ContentResolver来操作,那contentProvider是什么时候启动的呢?
二、启动
我们都知道,java有一个入口方法main,那Android的main方法在哪里呢?Activity?不,他在ActivityThread类里,我们来看看这个特殊的方法
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("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
这个方法里面主要是启动application已经初始化消息队列等工作,其中application调用了attach方法,在attach方法里面跨进程调用了我们的老朋友AMS(ActivityManagerService)的attachApplication方法
private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
ViewRootImpl.addFirstDrawHandler(new Runnable() {
@Override
public void run() {
ensureJitEnabled();
}
});
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
//..........
}
转到AMS里,发现他自调用了attachApplicationLocked,这个方法做了很多初始化操作,我们并将这些信息通过bindApplication跨进程传给了ApplicationThread(ActivityThread的内部类)
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());
applicationThread按惯例通过H这个Handler传给Activitythread
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<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);
}
}
// 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);
}
}
这里我们只关注ContentProvider的逻辑。看到installContentProviders了吗?名字已经暴露了他的功能,在这个方法执行后,才执行的application的onCreate方法,所以,ContentProvider是先于Application执行oncreate的。我们接着看installContentProviders
,它里面没啥逻辑,直接调用了installProvider
private IActivityManager.ContentProviderHolder installProvider(Context context,
IActivityManager.ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
//.............
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);
} catch (java.lang.Exception e) {
if (!mInstrumentation.onException(null, e)) {
throw new RuntimeException(
"Unable to get provider " + info.name
+ ": " + e.toString(), e);
}
return null;
}
} else {
provider = holder.provider;
if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "
+ info.name);
}
//下面是将provider保存的代码...
这个方法用ClassLoader创建了Provider,并调用了他的attachInfo方法,然后保存到了map里面。我们继续看attachInfo
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
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();
}
}
最后一行代码说明,contentProvider已经create。是的,ContentProvider是伴随进程启动而自行启动的,也就是我们的Main方法。
到这里,ContentProvider启动源码已经讲完,但他的增删改查方法是如何实现的呢,我们继续往下看
三、增删改查
ContentProvider的操作原理都是一样的,我这里拿query来说明。因为是ContentResolver来实现的,所以我们走进ContentResolver的query方法
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);
} catch (DeadObjectException e) {
// The remote process has died... but we only hold an unstable
// reference though, so we might recover!!! Let's try!!!!
// This is exciting!!1!!1!!!!1
unstableProviderDied(unstableProvider);
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
return null;
}
qCursor = stableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
}
//............
}
}
两种可能,通过unstableProvider或stableProvider去实现,他们其实都是通过acquireProvider来获取的,我们看一下这个方法
protected abstract IContentProvider acquireProvider(Context c, String name);
是一个抽象方法,由子类去实现的。我们通过getContentResolver获取的是ContextImpl的成员mContentResolver,发现这个mContentResolver是ApplicationContentResolver的实例,ApplicationContentResolver实现了ContentResolver,所以很自然走到ApplicationContentResolver里的acquireProvider方法
protected IContentProvider acquireProvider(Context context, String auth) {
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}
交给了ActivityThread来处理
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;
}
看过我前几篇的童鞋们很容易就发现这里跨进程转给了AMS的getContentProvider方法,然后转给getContentProviderImpl
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {
//省略巨量代码...........
// Use existing process if already started
checkTime(startTime, "getContentProviderImpl: looking for process record");
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;
}
}
}
这个方法先检查了是否已经启动了ContentProvider,如果没有则调用startProcessLocked去获取,这个方法有很多重载的方法,不过调来调去最后会调用Process.start(),这个start方法就是启动进程用的,具体实现我就不写了,毕竟本文重点不在那里。
好的,进程启动了,自然会调用ActivityThread里的入口main方法,也就是我们第二部分的内容了,将ContentProvider启动起来了。
回到我们第三部分开头,(un)stableProvider取得之后要调用他的query方法,发现他是个IContentProvider,我们跨进程需要的接口,我们ContentProviderNative实现了他也就是他的”stub”类,真正的实现者肯定继承了这个“stub”类,没错,他就在ContentProvider里:Transport。在这个类里我们找到了query方法并调用了ContentProvider的query
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, null) != 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源码就分析完了,按惯例放上一张图辅助理解。
四、总结
四大组件篇的源码分析圆满结束,到这里我们来进行下总结:
1、Binder:四大组件的IPC通信基本上是基于Binder,可见Binder在Android 的重要地位。AMS这个类就是很好的体现,是四大组件跨进程通信的基石。
2、ContextWrapper/ContextImpl:Activity和Service都是他的子类,其他两个启动都是通过他来实现,其实他们也就是我们口中的上下文(Context)保存了大量信息。而ContextWrapper所有操作都基本由ContextImpl来实现,这个经典的装饰模式也值得我们学习、深思。
3、H:这个Handler类诠释了Android的消息机制,他的原理实现也是我们必修的一门课,也是我的下一个剖析目标。
4、其他还需要进一步学习,掌握整个Android框架,对我们理解、处理Android问题会有极大的提升。