Android四大组件——ContentProvider的工作过程

ContentProvider是内容提供者,对外提供数据。内部运行依赖Binde机制。想要自己写一个ContentProvider向外部提供数据,需要继承ContentProvider并重写一下六个方法,在ContentProvider中提供了增删改查的方法,一般这个增删改查会通过SqliteDataBase这个数据库的方式对外提供增删改查的功能。也可以不这么做。

@Override
public boolean onCreate() {
    return false;
}

@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
    return null;
}

@Nullable
@Override
public String getType(@NonNull Uri uri) {
    return null;
}

@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
    return null;
}

@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
    return 0;
}

@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
    return 0;
}

需要在AndroidManifest.xml中去注册:
authorities 相当于是个地址
permission 用于控制权限

<provider
    android:name=".provider.BookProvider"
    android:authorities="com.sososeen09.knowledge.provider.book"
    android:permission="com.sososeen09.PROVIDER"
    android:process=":provider"/>

其它客户端查询,需要用到ContentResolver对象,ContentResolver是抽象类,它的实现是ContextImpl中的ApplicationContentResolver,是在ContextImpl的构造方法中创建的对象。

Uri bookUri = Uri.parse("content://com.sososeen09.knowledge.provider.book/book");
ContentValues values = new ContentValues();
values.put("_id", 6);
getContentResolver().insert(bookUri, values);
Cursor bookCursor = getContentResolver().query(bookUri, new String[]{"_id", "name"}, null, null, null);

Context的getContentResolver方法实际上获取的就是ContextImpl的ApplicationContentResolver类型的成员mContentResolver。

当ContentProvider所在进程还未创建时,第一次访问ContentProvider会触发ContentProvider所在进程的创建和ContentProvider对象的创建。通过ContentProvider的4个方法中的任何一个都会触发ContentProvider的启动过程。

源码分析

我们分析getContentResolver().query 方法触发的流程。

ContextWrapper的getContentResolver的方法如下,还是通过mBase来获取,这个mBase实际上是ContextImpl对象。

@Override
public ContentResolver getContentResolver() {
    return mBase.getContentResolver();
}

来看一下ContextImpl的getContentResolver方法,直接返回了一个成员变量mContentResolver。

@Override
public ContentResolver getContentResolver() {
    return mContentResolver;
}

这个mContentResolver实际上是ContentResolver的子类ApplicationContentResolver,该对象是在ContextImpl的构造方法中创建的。

private ContextImpl(ContextImpl container, ActivityThread mainThread,
                    LoadedApk packageInfo, IBinder activityToken, UserHandle user, int flags,
                    Display display, Configuration overrideConfiguration, int createDisplayWithId) {

...
    mContentResolver = new ApplicationContentResolver(this, mainThread, user);
}

获取了ContentResolver对象之后,调用它的query方法。在ContentResolver的query方法中首先会调用acquireUnstableProvider来获取一个IContentProvider对象。

在这里要说一下,返回值是IContentProvider对象,它继承自IInterface,也就是说它的实现类会是一个Binder对象。这也是因此ContentProvider不是一个Binder对象,而且一般ContentProvider和客户端不再同一个进程中。它的增删改查方法的结果肯定是需要一个通过一个Binder对象来进行IPC过程的传输。在这里就是IContentProvider。在这里它的实现是ContentProviderNative的子类ContentProvider.Transport。ContentProvider.Transport对象运行在ContentProvider的同一个进程,在远程有个特代理是ContentProviderProxy。

acquireUnstableProvider方法由ApplicationContentResolver实现,在ApplicationContentResolver中还有一个重写的方法是acquireProvider,它们都是做一个中转,调用的是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) {
        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;
}

可以看到,它首先调用acquireExistingProvider,查看是否有缓存的IContentProvider对象,如果有的话就直接返回这个缓存对象。缓存存放在mProviderMap这个Map集合中。

// The lock of mProviderMap protects the following variables.
final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
        = new ArrayMap<ProviderKey, ProviderClientRecord>();

如果当前没有缓存的话,就需要通过一个IPC过程调用AMS的getContentProvider方法来获取一个ContentProviderHolder,ContentProviderHolder实现了Parcelable接口,因此它可以被进程间传递,它就是客户端的ContentProvider的一个代理对象的包装类。之后调用installProvider方法还要修改ContentProvider的引用计数器。

AMS的getContentProvider方法又会调用getContentProviderImpl方法,在该方法中会判断所要请求的ContentProvider进程是否已经存在,如果不存在的话就会调用startProcessLocked方法来创建ContentProvider的进程并安装ContentProvider。

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
                                                     String name, IBinder token, boolean stable, int userId) {
    ContentProviderRecord cpr;
    ContentProviderConnection conn = null;
    ProviderInfo cpi = null;

    synchronized(this) {
        long startTime = SystemClock.uptimeMillis();

        ProcessRecord r = null;
      ...
        boolean checkCrossUser = true;

        checkTime(startTime, "getContentProviderImpl: getProviderByName");

        // First check if this content provider has been published...
        cpr = mProviderMap.getProviderByName(name, userId);
        // If that didn't work, check if it exists for user 0 and then
        // verify that it's a singleton provider before using it.
   ...
        boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;
   ...
        if (!providerRunning) {
            ...
                    ProcessRecord proc = getProcessRecordLocked(
                            cpi.processName, cpr.appInfo.uid, false);
                    if (proc != null && proc.thread != null && !proc.killed) {
                        ...
                                proc.thread.scheduleInstallProvider(cpi);
                           
                        }
                    } else {
                        proc = startProcessLocked(cpi.processName,
                                cpr.appInfo, false, 0, "content provider",
                                new ComponentName(cpi.applicationInfo.packageName,
                                        cpi.name), false, false, false);
                       ...
                        }
                    }
                    cpr.launchingApp = proc;
                    mLaunchingProviders.add(cpr);
                } finally {
                    Binder.restoreCallingIdentity(origId);
                }
            }
...
    }

 ...
    return cpr != null ? cpr.newHolder(conn) : null;
}

从方法可以看出,如果ContentProvider没有在运行,而且它所在的进程没有存在的话就会调用AMS自身的startProcessLocked方法来创建进程。AMS的startProcessLocked会调用Process的start方法,在该方法表明所要启动一个新进程,并且调用android.app.ActivityThread的main方法作为入口。

private final void startProcessLocked(ProcessRecord app, String hostingType,
                                      String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
  ...
        // Start the process.  It will either succeed and return a result containing
        // the PID of the new process, or else throw a RuntimeException.
        boolean isActivityProcess = (entryPoint == null);
        if (entryPoint == null) entryPoint = "android.app.ActivityThread";
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
                app.processName);
        checkTime(startTime, "startProcess: asking zygote to start proc");
        Process.ProcessStartResult startResult = Process.start(entryPoint,
                app.processName, uid, uid, gids, debugFlags, mountExternal,
                app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                app.info.dataDir, entryPointArgs);
     ... 
}

App进程启动的流程如下,这里我们就不细讲了。


App进程启动的流程.png

最终ActivityThread的main方法会被调用,该方法中会创建ActivityThread对象,并创建主线程的Looper对象,调用ActivityThread的attach方法,并且传递的参数为false,表明不是系统进程。之后就开始消息循环了。

public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
    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());

    // 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"));
    }

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

在ActivityThread的attach方法中,会通过一个IPC过程调用AMS的attachApplication方法。

private void attach(boolean system) {
    sCurrentActivityThread = this;
    mSystemThread = system;
    if (!system) {
    ...
        final IActivityManager mgr = ActivityManagerNative.getDefault();
        try {
            mgr.attachApplication(mAppThread);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
  ...
    } else {
     ...
    }

   ...
}

AMS的attachApplication方法又会调用其attachApplicationLocked方法,在该方法中又会通过IPC过程调用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<String, IBinder> services, Bundle coreSettings) {

    if (services != null) {
        // 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;
    sendMessage(H.BIND_APPLICATION, data);
}

AppBindData是一个JavaBean独享,用于ActivityThreadThread绑定的App的信息。
ApplicationThread的bindApplication方法是在Binder线程池中执行,因此通过一个Handler发送
H.BIND_APPLICATION消息切换到主线程。在Handler H的handleMessage方法中会从msg中取出AppBindData对象,并且调用ActivityThread的handleBindApplication方法。

在这里面做了5步操作:

  1. 创建ContextImpl对象
  2. 创建Instrumentation对象
  3. 创建Application对象
  4. 调用installContentProviders(app, data.providers)方法启动当前进程的ContentProvider并调用其onCreate方法
  5. 调用Application的onCreate方法
private void handleBindApplication(AppBindData data) {
    
...
    // If the app is Honeycomb MR1 or earlier, switch its AsyncTask
    // implementation to use the pool executor.  Normally, we use the
    // serialized executor as the default. This has to happen in the
    // main thread so the main looper is set right.
    if (data.appInfo.targetSdkVersion <= android.os.Build.VERSION_CODES.HONEYCOMB_MR1) {
        AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }

...

    data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);

   ...

    // Instrumentation info affects the class loader, so load it before
    // setting up the app context.
    final InstrumentationInfo ii;
    if (data.instrumentationName != null) {
        try {
            ii = new ApplicationPackageManager(null, getPackageManager())
                    .getInstrumentationInfo(data.instrumentationName, 0);
        } catch (PackageManager.NameNotFoundException e) {
            throw new RuntimeException(
                    "Unable to find instrumentation info for: " + data.instrumentationName);
        }

        mInstrumentationPackageName = ii.packageName;
        mInstrumentationAppDir = ii.sourceDir;
        mInstrumentationSplitAppDirs = ii.splitSourceDirs;
        mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii);
        mInstrumentedAppDir = data.info.getAppDir();
        mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
        mInstrumentedLibDir = data.info.getLibDir();
    } else {
        ii = null;
    }
    // 1 创建 ContextImpl对象
    final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
...
    // 2 创建Instrumentation对象
    // Continue loading instrumentation.
    if (ii != null) {
        final ApplicationInfo instrApp = new ApplicationInfo();
        ii.copyTo(instrApp);
        instrApp.initForUser(UserHandle.myUserId());
        final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                appContext.getClassLoader(), false, true, false);
        final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);

        try {
            final ClassLoader cl = instrContext.getClassLoader();
            mInstrumentation = (Instrumentation)
                    cl.loadClass(data.instrumentationName.getClassName()).newInstance();
        } catch (Exception e) {
            throw new RuntimeException(
                    "Unable to instantiate instrumentation "
                            + data.instrumentationName + ": " + e.toString(), e);
        }

        final ComponentName component = new ComponentName(ii.packageName, ii.name);
        mInstrumentation.init(this, instrContext, appContext, component,
                data.instrumentationWatcher, data.instrumentationUiAutomationConnection);

...
        }
    } else {
        mInstrumentation = new Instrumentation();
    }

...// 3 创建 Applcation对象
    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

        
        // 4 启动当前进程的ContentProvider,并调用其方法
        if (!data.restrictedBackupMode) {
            if (!ArrayUtils.isEmpty(data.providers)) {
                installContentProviders(app, data.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);
        }
...     // 5 调用Application的onCreate方法
        try {
            mInstrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
     ...
    }
}

我们来看一下ActivityThread的installContentProviders方法。在该方法中主要遍历当前进程的ProviderInfo信息,通过调用installProvider方法来构建一个IActivityManager.ContentProviderHolder对象。

private void installContentProviders(
        Context context, List<ProviderInfo> providers) {
    final ArrayList<IActivityManager.ContentProviderHolder> results =
            new ArrayList<IActivityManager.ContentProviderHolder>();

    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) {
        throw ex.rethrowFromSystemServer();
    }
}

还记得我们之前说的吗,ContentProvider对象是无法跨进程调用的,因此需要一个Binder对象用于给客户端使用。ContentProviderHolder实现了Pacelable接口,可以在进程间传递数据。它实际上就是一个JavaBean对象,但是里面封装了IContentProvider,它实际上会作为一个Binder对象用于进程间方法的调用。我们前面也已经提了。IContentProvider的实现是ContentProviderNative的子类ContentProvider.Transport。ContentProvider.Transport对象运行在ContentProvider的同一个进程,在远程有个特代理是ContentProviderProxy。

public static class ContentProviderHolder implements Parcelable {
    public final ProviderInfo info;
    public IContentProvider provider;
    public IBinder connection;
    public boolean noReleaseNeeded;
...
}

我们来看一下ActivityThread的installProvider方法,可以看到首先通过类加载器创建了ContentProvider对象,然后调用了ContentProvider的attachInfo方法。

private IActivityManager.ContentProviderHolder installProvider(Context context,
                                                               IActivityManager.ContentProviderHolder holder, ProviderInfo info,
                                                               boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ContentProvider localProvider = null;
    IContentProvider provider;
    if (holder == null || holder.provider == null) {
       ...
        Context c = null;
        ApplicationInfo ai = info.applicationInfo;
        if (context.getPackageName().equals(ai.packageName)) {
            c = context;
        } else if (mInitialApplication != null &&
                mInitialApplication.getPackageName().equals(ai.packageName)) {
            c = mInitialApplication;
        } else {
            try {
                c = context.createPackageContext(ai.packageName,
                        Context.CONTEXT_INCLUDE_CODE);
            } catch (PackageManager.NameNotFoundException e) {
                // Ignore
            }
        }
        ...
        try {
            final java.lang.ClassLoader cl = c.getClassLoader();
            localProvider = (ContentProvider)cl.
                    loadClass(info.name).newInstance();
            provider = localProvider.getIContentProvider();
...
            // XXX Need to create the correct context for this provider.
            localProvider.attachInfo(c, info);
        } catch (java.lang.Exception e) {
            ...
        }
    } else {
        provider = holder.provider;
        if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "
                + info.name);
    }

 ...
    return retHolder;
}

我们再来看一下ContentProvider的attachInfo方法,该方法会调用3个参数的重载方法,在该方法中调用了ContextProvider的onCreate方法。

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的onCreate方法是在Application的onCreate方法之前调用的。当然了,还是要晚于Application的attachBaseContext方法。

ContentProvider创建完毕并启动之后还没有完事,会通过一个IPC过程调用AMS的publishContentProviders方法,此时传递ApplicationThread对象和创建完毕的ContentProviderHolder集合。publishContentProviders方法中也即是在AMS端存一下ContentProvider的记录,这里就不再细说了。

到此,ContentProvider所在的进程已经创建完毕,并且ContentProvider也已经全部启动起来。但是还没有完,调起ContentResolver发起query方法的那边还在等着返回结果呢。回到ContentResolver的query方法。注意此时客户端获取的IContentProvider对象是ContentProviderProxy,它是ContentProviderNative的内部类,通过IPC过程调用到ContentProviderNative.ContentProvider.Transport中。

# android.content.ContentResolver
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
                                    @Nullable String[] projection, @Nullable String selection,
                                    @Nullable String[] selectionArgs, @Nullable String sortOrder,
                                    @Nullable CancellationSignal cancellationSignal) {
    Preconditions.checkNotNull(uri, "uri");
    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    if (unstableProvider == null) {
        return null;
    }
    IContentProvider stableProvider = null;
    Cursor qCursor = null;
    try {
  ...
        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);
        }
        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, selection, sortOrder);

        // 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;
  ...
}

再来看一下ContentProvider.Transport的query方法,由于ContentProvider.Transport是非静态内部类,因此持有ContentProvider的引用,所以内部调用了ContentProvider的query方法的返回值,返回的结果也会通过一个IPC过程返回到调用者那里。ContentProvider对外提供数据的方法,query、insert、delete、update方法也都是通过Transport进行调用的。

# android.content.ContentProvider.Transport
@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, 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 = ContentProvider.this.query(uri, projection, selection,
                selectionArgs, sortOrder, CancellationSignal.fromTransport(
                        cancellationSignal));
        if (cursor == null) {
            return null;
        }

        // Return an empty cursor for all columns.
        return new MatrixCursor(cursor.getColumnNames(), 0);
    }
    final String original = setCallingPackage(callingPkg);
    try {
        return ContentProvider.this.query(
                uri, projection, selection, selectionArgs, sortOrder,
                CancellationSignal.fromTransport(cancellationSignal));
    } finally {
        setCallingPackage(original);
    }
}

总结

  1. 访问一个ContentProvider需要用到ContentResolver对象,它是一个抽象类,实际上用的是它的实现类ContextImpl的ApplicationContentResolver。

  2. 访问ContextProvider时一个IPC过程,由于ContentProvider不能跨进程访问。调用ContentResolver的query等方法首先需要从AMS端获取一个ContextProvider的代理对象,这个代理对象实现了IContentProvider接口,它用于代理远端的Binder对象ContextProvider.Trasnport。ContextProvider.Trasnport继承自ContentProviderNative,ContentProviderNative实现了IContentProvider接口。通过ContextProvider.Trasnport的本地代理对象,可以调用ContextProvider.Trasnport的query方法,Trasnport是ContextProvider的内部类,持有ContextProvider的引用,它再去调用ContextProvider的方法并通过IPC过程返回结果。

  3. 第一次访问ContentProvider的时候,如果它还没有创建,AMS会通过startProcessLock方法调用Process的start方法,通过Zygote去fork一个进程之后调用ActivityThread的main方法,在main方法中创建ActivityThread对象,并调用其attach方法,在此过程创建了Instrumentation对象、Application对象,并创建该进程中对应的ContentProvider并调用其onCreate方法,此后调用Application的onCreate方法,因此ContentProvider的onCreate方法早于Application的onCreate方法执行,这也是四大组件中唯一一个比较特别的。

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

推荐阅读更多精彩内容