本文出自门心叼龙的博客,属于原创类容,转载请注明出处。
今天写的这篇已经源码解析的第六篇了,虽然这类文章不如实战类文章受众那么广,但是作为每个Android开发工程师来讲,加强内功修炼这是作为向高级工程师迈进的必经之路。要知道了解了底层的工作原理对于以后实战开发中出现的各种各样的问题都会轻易解决。这和习武是一个道理,如果一个人它的内功很强,那它学什么都很快很容易掌握,否则它永远只是个花架子。
好了,言归正传,我们在日常开发过程中经常需要访问照片应用中图片资源,或者读取通讯录应用当中的联系人信息等等的一些应用场景,这时候就需要用到ContentProvider,ConentProvider在Android四大组件中排行老四,它是一个国家图书馆,为不同的应用程序之间的数据共享提供了统一的访问接口,它需要和ContentResolver配合使用,ContentProvider负责提供数据,ContentResolver负责获取数据。在应用程序启动的时候,ContentProvider就会被初始化注册到ActivityManagerServcie,然后其他的应用通过uri向服务端获取ConentProvider所对应的Transport对象。Transport本质上是一个binder,有了binder这个中间人对象,我们就可以调用远程的ConentProvider所提供的方法了。和之前一样,我先先来回顾一下ContentProvider的简单使用,然后在对它的源码进行解析。
ContentProvider的基本用法
定义ContentProvider
自定义一个ContentProvider很简单,我只需要继承ContentProvider,并复写的它的增删改查方法即可,具体代码实现如下:
public class MyContentProvider extends ContentProvider {
public MyContentProvider() {
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// Implement this to handle requests to delete one or more rows.
Log.v("MYTAG", "delete");
return 0;
}
@Override
public String getType(Uri uri) {
Log.v("MYTAG", "getType");
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.v("MYTAG", "insert...");
return null;
}
@Override
public boolean onCreate() {
Log.v("MYTAG", "onCreate...");
Log.v("MYTAG", "currThread:" + Thread.currentThread().getName());
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Log.v("MYTAG", "query...");
Log.v("MYTAG", "currThread:" + Thread.currentThread().getName());
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
Log.v("MYTAG", "update...");
return 0;
}
}
为了测试方便,我们的方法只要调通即可,所以相关方法了只是简单的打印了几行日志,并没有做具体的代码逻辑实现。
注册ContentProvider
定义完毕了ContentProvider,接下来的工作就是要在清单文件AndroidMnifest.xml里面进行注册,如下所示:
通过name指定ContentProvider所在的路径,authorities作为ContentProvider唯一的身份标识,外界也就是通过它来访问ContentProvider的,process为ContentProvider指定所在的进程,exported属性表示是否允许外部应用访问,默认为true。这样服务端的工作就做完了,现在我们看客户端怎么来调用:
ContentProvider的调用
Uri uri = Uri.parse("content://com.test.provider");
getContentResolver().query(uri,null,null,null,null);
getContentResolver().query(uri,null,null,null,null);
getContentResolver().query(uri,null,null,null,null);
首先通过Uri对象的parse方法构建了一个uri,这个参数就是我们在AndroidMinifest.xml清单文件里面定义的authorities属性的值,然后我们以query方法为例调用了三次,打印日志如下:
2019-11-21 10:49:19.148 4752-4764/? V/MYTAG: query...
2019-11-21 10:49:19.148 4752-4764/? V/MYTAG: currThread:Binder:4752_1
2019-11-21 10:49:19.150 4752-4766/? V/MYTAG: query...
2019-11-21 10:49:19.150 4752-4766/? V/MYTAG: currThread:Binder:4752_3
2019-11-21 10:49:19.150 4752-4766/? V/MYTAG: query...
2019-11-21 10:49:19.150 4752-4766/? V/MYTAG: currThread:Binder:4752_3
query方法响应了,调用了三次,发现他们是在两个线程中执行的,说明服务器端是一个binder线程池,这一点需要注意一下。
ConentProvider的注册过程
以上就是整个ConentProvider的简单使用过程,接下来我们看看它的背后的工作原理。注册首先是从ActivityThrad的main方法开始的,然后直到ConentProvider的onCreate方法被回调,此时就标志整个注册过程完成。接下来我们看ActivityThread的main方法:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// Install selective syscall interception
AndroidOs.install();
// 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();
// 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();
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
//注释1
ActivityThread thread = new ActivityThread();
//注释2
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
//注释3
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
我们知道应用程序所在的进程被创建完毕,紧接着就会执行ActivityThread的main方法初始化应用,在main方法里主要做了这么几个工作,首先在注释1处创建了一个ActivityThread 对象,然后在注释2处挂载了应用程序,紧接着在注释3处创建主线程的消息管理器。应用程序的ContentProvider就是在ActivityThread的注释2处的attach方法所初始化的。该方法的实现如下所示:
private void attach(boolean system, long startSeq) {
...
android.ddm.DdmHandleAppName.setAppName("",
UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManager.getService();
try {
//初始1
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
...
这个方法有些长,我们只看关键部分,注释1处它调用了ActivityManager.getService()对象的attachApplication方法,ActivityManager.getService在前面我们已经多次提到过,这不在重复解释,它就是ActivityManagerService,顺藤摸瓜我们继续往下走,ActivityManagerService的attachApplication方法如下所示:
ActivityManagerService中的流程
@Override
public final void attachApplication(IApplicationThread thread, long startSeq) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid, callingUid, startSeq);
Binder.restoreCallingIdentity(origId);
}
}
ActivityManagerService的attachApplication方法几乎什么都没有做,直接就把挂载app的工作交给了自己的attachApplicationLocked,该方法如下所示:
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid, int callingUid, long startSeq) {
...
if (app.isolatedEntryPoint != null) {
// This is an isolated process which should just call an entry point instead of
// being bound to an application.
thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs);
} else if (instr2 != null) {
//注释1
thread.bindApplication(processName, appInfo, providers,
instr2.mClass,
profilerInfo, instr2.mArguments,
instr2.mWatcher,
instr2.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.isPersistent(),
new Configuration(app.getWindowProcessController().getConfiguration()),
app.compat, getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, autofillOptions, contentCaptureOptions);
} else {
thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.isPersistent(),
new Configuration(app.getWindowProcessController().getConfiguration()),
app.compat, getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, autofillOptions, contentCaptureOptions);
}
...
}
attachApplicationLocked这个方法还是比较长的,我们直接看注释1处,此时调用了ApplicationThread的bindApplication方法,该方法的实现如下:
ActivityThread中的流程
ApplicationThread的bindApplication方法的实现如下所示:
public final void bindApplication(String processName, ApplicationInfo appInfo,
List 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, AutofillOptions autofillOptions,
ContentCaptureOptions contentCaptureOptions) {
if (services != null) {
if (false) {
// Test code to make sure the app could see the passed-in services.
for (Object oname : services.keySet()) {
if (services.get(oname) == null) {
continue; // AM just passed in a null service.
}
String name = (String) oname;
// See b/79378449 about the following exemption.
switch (name) {
case "package":
case Context.WINDOW_SERVICE:
continue;
}
if (ServiceManager.getService(name) == null) {
Log.wtf(TAG, "Service " + name + " should be accessible by this app");
}
}
}
// Setup the service cache in the ServiceManager
ServiceManager.initServiceCache(services);
}
setCoreSettings(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.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;
sendMessage(H.BIND_APPLICATION, data);
}
此时构建了一个AppBindData并把它传递给了ActivityThread的消息管理器Handler了,收到消息后会调用ActivityThread的handleBindApplication方法,该方法的具体实现如下:
private void handleBindApplication(AppBindData data) {
...
Application app;
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();
try {
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
//注释1
app = data.info.makeApplication(data.restrictedBackupMode, null);
// Propagate autofill compat state
app.setAutofillOptions(data.autofillOptions);
// Propagate Content Capture options
app.setContentCaptureOptions(data.contentCaptureOptions);
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)) {
//注释2
installContentProviders(app, data.providers);
}
}
// 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 {
//注释3
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
...
handleBindApplication方法的实现有些长,我们只看关键部分,在注释1处Application创建了,在注释2处我们终于找到了初始化ContentProvider的真正方法installContentProviders,在注释3处Application的onCreate被回调了,我们有没有发现ContentProvider是先于Application初始化的,下面我主要看ContentProvider初始化的核心方法installContentProviders,该方法的具体实现如下:
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());
}
//注释1
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
//注释2
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
installContentProviders方法的实现不是很复杂,就做了一件事就是初始化了一个ContentProviderHolder集合,并把它传递给服务端,ContentProviderHolder具体是什么,我们主要看注释1处的installProvider方法,该方法的具体实现如下:
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
...
final java.lang.ClassLoader cl = c.getClassLoader();
LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
if (packageInfo == null) {
// System startup case.
packageInfo = getSystemContext().mPackageInfo;
}
//注释1
localProvider = packageInfo.getAppFactory()
.instantiateProvider(cl, info.name);
//注释2
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.
//注释3
localProvider.attachInfo(c, info);
...
if (localProvider != null) {
ComponentName cname = new ComponentName(info.packageName, info.name);
ProviderClientRecord pr = mLocalProvidersByName.get(cname);
if (pr != null) {
if (DEBUG_PROVIDER) {
Slog.v(TAG, "installProvider: lost the race, "
+ "using existing local provider");
}
provider = pr.mProvider;
} else {
//注释4
holder = new ContentProviderHolder(info);
//注释5
holder.provider = provider;
holder.noReleaseNeeded = true;
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
mLocalProviders.put(jBinder, pr);
mLocalProvidersByName.put(cname, pr);
}
retHolder = pr.mHolder;
}
...
return retHolder;
}
在注释1处调用了packageInfo.getAppFactory()对象的instantiateProvider方法,我们看该方法的实现:
public @NonNull ContentProvider instantiateProvider(@NonNull ClassLoader cl,
@NonNull String className)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return (ContentProvider) cl.loadClass(className).newInstance();
}
在instantiateProvider方法里ContentProvider终于通过反射实例化了。
在注释2处调用了ContentProvider的getIContentProvider方法,我们看的具体实现是什么:
private Transport mTransport = new Transport();
public IContentProvider getIContentProvider() {
return mTransport;
}
返回了一个Transport对象,我们来看下它的实现:
class Transport extends ContentProviderNative {}
abstract public class ContentProviderNative extends Binder implements IContentProvider {}
通过继承关系我们发现Transport继承了ContentProviderNative ,而ContentProviderNative又继承了Binder并实现了IContentProvider接口,所以注释2处的provider的实现就是Transport,而它就是一个binder,这个binder会在注释5处给他的ContentProviderHolder对象的provider属性, installProvider方法最后返回了持有ContentProvider的Binder对象的ContentProviderHolder。
然后我们在看注释3处调用了ContentProvider的attachInfo方法,它的实现如下:
public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);
}
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
mNoPerms = testing;
mCallingPackage = new ThreadLocal<>();
/*
* 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 != 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();
}
}
两个参数的attachInfo方法重载了三个参数的attachInfo方法,在该方法的最后一行,终于发现我们的ContentProvider的onCreate方法被调用了,这就意味着ContentProvider的启动已经完成了。
现在我们再看installContentProviders方法的注释2处调用了ActivityManager.getService()对象的publishContentProviders方法,并把客户端生成的ContentProviderHolder对象传递给了服务端。我们再看ActivityManagerService对象的publishContentProviders方法的具体实现:
ActivityManagerService中的流程
public final void publishContentProviders(IApplicationThread caller,
List providers) {
if (providers == null) {
return;
}
enforceNotIsolatedCaller("publishContentProviders");
synchronized (this) {
final ProcessRecord r = getRecordForAppLocked(caller);
if (DEBUG_MU) Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when publishing content providers");
}
final long origId = Binder.clearCallingIdentity();
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 (DEBUG_MU) Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
if (dst != null) {
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
mProviderMap.putProviderByClass(comp, dst);
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.putProviderByName(names[j], dst);
}
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);
}
// Make sure the package is associated with the process.
// XXX We shouldn't need to do this, since we have added the package
// when we generated the providers in generateApplicationProvidersLocked().
// But for some reason in some cases we get here with the package no longer
// added... for now just patch it in to make things happy.
r.addPackage(dst.info.applicationInfo.packageName,
dst.info.applicationInfo.longVersionCode, mProcessStats);
synchronized (dst) {
dst.provider = src.provider;
dst.setProcess(r);
dst.notifyAll();
}
updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER);
maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
src.info.authority);
}
}
Binder.restoreCallingIdentity(origId);
}
}
这个方法的主要作用就是把客户端传递过来的ContentProviderHolder对象保存到了ContentProviderRecord对象。而通过上面的分析我们知道ContentProviderHolder里面存储就是ContentProvider所对应的Binder对象,所以其他应用通过uri所查找的就是ActivityManagerService里面ContentProvider的Binder,拿到了Binder对象就可以远程调用ContentProvider里面的方法了。好了,截止目前ContentProvider的注册就彻底讲完了,接下来我们看ConentResolver方法的调用。
ConentResolver的调用过程
ConentResolver中的流程
ConentProvider主要给我们提供了insert,delete,update,query四个方法,我们以query方法为例进行研究, 基本用法如下:
Uri uri = Uri.parse("content://com.test.provider");
getContentResolver().query(uri,null,null,null,null);
ContentWraper的getContentResolver方法实现如下:
@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}
不用说了mBase就是ContentImpl,getContentResolver方法如下所示:
private final ApplicationContentResolver mContentResolver;
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
而mContentResolver就是一个ApplicationContentResolver,我们看看他的具体实现:
private static final class ApplicationContentResolver extends ContentResolver {
@UnsupportedAppUsage
private final ActivityThread mMainThread;
public ApplicationContentResolver(Context context, ActivityThread mainThread) {
super(context);
mMainThread = Preconditions.checkNotNull(mainThread);
}
@Override
@UnsupportedAppUsage
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, getUserId());
}
}
ApplicationContentResolver是ContentResolver的一个具体实现类,它位于ContextImpl的内部。所以调用getContentResolver().query方法,其实就是调用了ApplicationContentResolver对象的query方法,该方法的具体实现如下:
public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder) {
return query(uri, projection, selection, selectionArgs, sortOrder, null);
}
public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder,
@Nullable CancellationSignal cancellationSignal) {
Bundle queryArgs = createSqlQueryBundle(selection, selectionArgs, sortOrder);
return query(uri, projection, queryArgs, cancellationSignal);
}
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal) {
Preconditions.checkNotNull(uri, "uri");
try {
if (mWrapped != null) {
return mWrapped.query(uri, projection, queryArgs, cancellationSignal);
}
} catch (RemoteException e) {
return null;
}
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,
queryArgs, 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);
//注释1
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
return null;
}
//注释2
qCursor = stableProvider.query(
mPackageName, uri, projection, queryArgs, remoteCancellationSignal);
}
if (qCursor == null) {
return null;
}
// Force query execution. Might fail and throw a runtime exception here.
qCursor.getCount();
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogQueryToEventLog(durationMillis, uri, projection, queryArgs);
// Wrap the cursor object into CursorWrapperInner object.
final IContentProvider provider = (stableProvider != null) ? stableProvider
: acquireProvider(uri);
final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
stableProvider = null;
qCursor = null;
return wrapper;
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
} finally {
if (qCursor != null) {
qCursor.close();
}
if (cancellationSignal != null) {
cancellationSignal.setRemote(null);
}
if (unstableProvider != null) {
releaseUnstableProvider(unstableProvider);
}
if (stableProvider != null) {
releaseProvider(stableProvider);
}
}
}
以上就是三个重载query的方法最终会调用最下面的这个query方法,首先我们看注释1处的acquireProvider方法,该方法具体实现如下所示:
@UnsupportedAppUsage
public final IContentProvider acquireProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {
return null;
}
final String auth = uri.getAuthority();
if (auth != null) {
return acquireProvider(mContext, auth);
}
return null;
}
protected IContentProvider acquireProvider(Context context, String auth) {
//注释1
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}
ContentResolver最终会调用注释1处的ActivityThrad的acquireProvider方法,该方法实现如下所示:
ActivityThrad中的流程
@UnsupportedAppUsage
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;
}
// 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.
ContentProviderHolder holder = null;
try {
synchronized (getGetProviderLock(auth, userId)) {
//注释2
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), c.getOpPackageName(), auth, userId, stable);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
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;
}
在注释1处如果能查询到则直接就返回了,如果查询不到会在服务端去查询在ContentProvider注册的时候所在服务端ActivityManagerService中所保存的ContentProviderHolder ,而最终返回IContentProvider就是ContentProvider的对象Transport对象,最终调用的是Transport对象的query方法,该方法的具体实现如下:
class Transport extends ContentProviderNative {
volatile AppOpsManager mAppOpsManager = null;
volatile int mReadOp = AppOpsManager.OP_NONE;
volatile int mWriteOp = AppOpsManager.OP_NONE;
//注释1
volatile ContentInterface mInterface = ContentProvider.this;
ContentProvider getContentProvider() {
return ContentProvider.this;
}
@Override
public String getProviderName() {
return getContentProvider().getClass().getName();
}
@Override
public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection,
@Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) {
uri = validateIncomingUri(uri);
uri = maybeGetUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
// The caller has no access to the data, so return an empty cursor with
// the columns in the requested order. The caller may ask for an invalid
// column and we would not catch that but this is not a problem in practice.
// We do not call ContentProvider#query with a modified where clause since
// the implementation is not guaranteed to be backed by a SQL database, hence
// it may not handle properly the tautology where clause we would have created.
if (projection != null) {
return new MatrixCursor(projection, 0);
}
// Null projection means all columns but we have no idea which they are.
// However, the caller may be expecting to access them my index. Hence,
// we have to execute the query as if allowed to get a cursor with the
// columns. We then use the column names to return an empty cursor.
Cursor cursor;
final String original = setCallingPackage(callingPkg);
try {
//注释2
cursor = mInterface.query(
uri, projection, queryArgs,
CancellationSignal.fromTransport(cancellationSignal));
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
setCallingPackage(original);
}
if (cursor == null) {
return null;
}
// Return an empty cursor for all columns.
return new MatrixCursor(cursor.getColumnNames(), 0);
}
Trace.traceBegin(TRACE_TAG_DATABASE, "query");
final String original = setCallingPackage(callingPkg);
try {
return mInterface.query(
uri, projection, queryArgs,
CancellationSignal.fromTransport(cancellationSignal));
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
} finally {
setCallingPackage(original);
Trace.traceEnd(TRACE_TAG_DATABASE);
}
}
...
在Transport的query方法的注释2处会调用mInterface对象的query方法,而mInterface就是我们注释1处的ContentProvider对象,此时ContentProvider对象的query方法就执行了,这就意味了ConentResolver的调用过程就彻底讲完了。
总结
最后为了方便大家理解,我画了两幅流程图。
ContentProvider注册流程图
ActivityThread.main
ActivityThread.attach
ActivityManagerService.attachApplication
ActivityManagerService.attachApplicationLocked
ApplicationThread.bindApplication
ActivityThread.handleBindApplication
ActivityThread.installContentProviders
ActivityThread.installProvider
ContentProvider.attachInfo
ContentProvider.onCreate
ContentResolver调用流程图
ContentResolver.query
ApplicationContentResolver.query
ActivityThread.acquireProvider
ActivityManagerService.getContentProvider
Transport.query
ContentProvider.query
问题反馈
在使用学习中有任何问题,请留言,或加入Android、Java开发技术交流群
- QQ群:810970432
- email:[email protected]