Android10.0 ContentProvider工作过程源码解析

本文出自门心叼龙的博客,属于原创类容,转载请注明出处。

今天写的这篇已经源码解析的第六篇了,虽然这类文章不如实战类文章受众那么广,但是作为每个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里面进行注册,如下所示:

   <provider
            android:name=".provider.MyContentProvider"
            android:authorities="com.test.provider"
            android:process=":remote"
            android:enabled="true"
            android:exported="true"/>

通过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("<pre-initialized>");
        
        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("<pre-initialized>",
                                                    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<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, 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<ProviderInfo> providers) {
                     
        final ArrayList<ContentProviderHolder> 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<ContentProviderHolder> 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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容