Android中的ContentProvider源码解析

本文系转载文章,阅读原文可读性会更好,文章末尾有原文链接

ps:本篇文章是基于 Android API 26来分析的

1、ContentProvider 初始化

在上一篇Android中的IPC进程通信方式第四篇中,我们学了用 ContentProvider 进行 IPC 通信,这一篇我们来分析 ContentProvider 的源码;当一个应用启动时,入口方法为 ActivityThread 的main方法,main 方法是一个静态方法,在 main 方法中会创建 ActivityThread 的实例并创建主线程的消息队列;ActivityThread 的 attach 方法中会远程调用 AMS 的 attachApplication 方法并将 ApplicationThread 对象提供给AMS;
public static void main(String[] args) {

    ......
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    ......

}

ActivityManager.getService() 这行代码通过 IBinder 进程间通讯调用 AMS,并且调用 AMS 中的 attachApplication 方法;
private void attach(boolean system) {

    ......
    final IActivityManager mgr = ActivityManager.getService();
    try {
        mgr.attachApplication(mAppThread);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
    ......

}

我们看看 ActivityManager 是如何获取 AMS 的,点击 ActivityManager.getService() 方法查看,发现得到了一个 IActivityManager,这里的 IActivityManager 实现类是 ActivityManagerService,也就是我们上面简称的 AMS;
public static IActivityManager getService() {

    return IActivityManagerSingleton.get();
}

private static final Singleton IActivityManagerSingleton =
        new Singleton() {
            @Override
            protected IActivityManager create() {
                final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                final IActivityManager am = IActivityManager.Stub.asInterface(b);
                return am;
            }
        };

再回过头来,我们查看 mgr.attachApplication(mAppThread) 代码,也就是 AMS 中的 attachApplication 方法,发现调用了 attachApplicationLocked 方法;

@Override
public final void attachApplication(IApplicationThread thread) {
    synchronized (this) {
        int callingPid = Binder.getCallingPid();
        final long origId = Binder.clearCallingIdentity();
        attachApplicationLocked(thread, callingPid);
        Binder.restoreCallingIdentity(origId);
    }
}

往下查看 ActivityManagerService 的 attachApplicationLocked 方法,下面的 thread 就是 ActivityThread,发现调用了 ActivityThread 的 bindApplication 方法,又回调到ActivityThread中进行处理了;
private final boolean attachApplicationLocked(IApplicationThread thread,

                                              int pid) {
    ......
    if (app.instr != null) {
        thread.bindApplication(processName, appInfo, providers,
                app.instr.mClass,
                profilerInfo, app.instr.mArguments,
                app.instr.mWatcher,
                app.instr.mUiAutomationConnection, testMode,
                mBinderTransactionTrackingEnabled, enableTrackAllocation,
                isRestrictedBackupMode || !normalMode, app.persistent,
                new Configuration(getGlobalConfiguration()), app.compat,
                getCommonServicesLocked(app.isolated),
                mCoreSettingsObserver.getCoreSettingsLocked(),
                buildSerial);
    } else {
        thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                null, null, null, testMode,
                mBinderTransactionTrackingEnabled, enableTrackAllocation,
                isRestrictedBackupMode || !normalMode, app.persistent,
                new Configuration(getGlobalConfiguration()), app.compat,
                getCommonServicesLocked(app.isolated),
                mCoreSettingsObserver.getCoreSettingsLocked(),
                buildSerial);
    }
    ......
    return true;

}

查看 ActivityThread 的 bindApplication 方法,发现把一些信息赋值给 AppBindData 对象的属性,然后通过 Handler 封装 AppBindData 发送一条消息出去;
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) {
    ......
    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;
    sendMessage(H.BIND_APPLICATION, data);

}

看一下 Handler 的子类 H 的处理,发现调用了 ActivityThread 的 handleBindApplication 方法;
private class H extends Handler {

    ......

    public void handleMessage(Message msg) {
        if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
        switch (msg.what) {
            ......
            case BIND_APPLICATION:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                AppBindData data = (AppBindData) msg.obj;
                handleBindApplication(data);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
        }
    }

}

查看 ActivityThread 的 handleBindApplication 方法,我们重点看 Application app = data.info.makeApplication(data.restrictedBackupMode, null)、installContentProviders(app, data.providers) 和 mInstrumentation.callApplicationOnCreate(app) 这3行代码;
private void handleBindApplication(AppBindData data) {

    ......
    try {
        // If the app is being launched for full backup or restore, bring it up in
        // a restricted environment with the base application class.
        Application app = data.info.makeApplication(data.restrictedBackupMode, null);
        mInitialApplication = app;

        // don't bring up providers in restricted mode; they may depend on the
        // app's custom Application class
        if (!data.restrictedBackupMode) {
            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);
        }
        catch (Exception e) {
            throw new RuntimeException(
                    "Exception thrown in onCreate() of "
                            + data.instrumentationName + ": " + e.toString(), e);
        }
        ......
    } finally {
        StrictMode.setThreadPolicy(savedPolicy);
    }
    ......

}

我们来看一下 data.info.makeApplication(data.restrictedBackupMode, null) 方法,data.info 其实是 LoadedApk 类对象,在这个 LoadedApk 类的 makeApplication 方法中,获取 ClassLoader,然后通过 mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext) 进行初始化 Application;
public Application makeApplication(boolean forceDefaultAppClass,

                                   Instrumentation instrumentation) {
    ......
    try {
        java.lang.ClassLoader cl = getClassLoader();
        if (!mPackageName.equals("android")) {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                    "initializeJavaContextClassLoader");
            initializeJavaContextClassLoader();
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);
        appContext.setOuterContext(app);
    } catch (Exception e) {
        ......
    }
    ......
    return app;

}

上面的 mActivityThread.mInstrumentation 其实是 Instrumentation,我们点击 Instrumentation 含有3个参数的 newApplication 方法;
public Application newApplication(ClassLoader cl, String className, Context context)

        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException {
    return newApplication(cl.loadClass(className), context);

}

再查看 Instrumentation 含有2个参数的 newApplication 方法,发现这里才是对 Application 初始化实现并将 Context 关联起来;
static public Application newApplication(Class clazz, Context context)

        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException {
    Application app = (Application)clazz.newInstance();
    app.attach(context);
    return app;

}

我们再回到 ActivityThread 中调用 installContentProviders(app, data.providers) 的这行代码并查看它的具体实现,发现调用了 ActivityThread 的 installProvider 方法;
private void installContentProviders(

        Context context, List providers) {
    ......
    for (ProviderInfo cpi : providers) {
        ......
        ContentProviderHolder cph = installProvider(context, null, cpi,
                false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
        if (cph != null) {
            cph.noReleaseNeeded = true;
            results.add(cph);
        }
    }
    ......

}

我们往下看 ActivityThread 的 installProvider 方法,发现初始化了 ContentProvider 调用了 ContentProvider 含有2个参数的 attachInfo 方法;
private ContentProviderHolder installProvider(Context context,

                                              ContentProviderHolder holder, ProviderInfo info,
                                              boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ......
    final java.lang.ClassLoader cl = c.getClassLoader();
            localProvider = (ContentProvider)cl.
                loadClass(info.name).newInstance();
            provider = localProvider.getIContentProvider();
            if (provider == null) {
                Slog.e(TAG, "Failed to instantiate class " +
                      info.name + " from sourceDir " +
                      info.applicationInfo.sourceDir);
                return null;
            }
            if (DEBUG_PROVIDER) Slog.v(
                TAG, "Instantiating local provider " + info.name);
            // XXX Need to create the correct context for this provider.
            localProvider.attachInfo(c, info);
    ......
    return retHolder;

}

往下看 ContentProvider 含有2个参数的 attachInfo 方法,发现调用了它自己含有3个参数的 attachInfo 方法;
public void attachInfo(Context context, ProviderInfo info) {

attachInfo(context, info, false);

}

点击查看 ContentProvider 含有3个参数的 attachInfo 方法,发现调用了 ContentProvider 的 onCreate 方法;在上面 ActivityThread 的 handleBindApplication 方法中,调用了3个最关键的方法,那就是 Application app = data.info.makeApplication(data.restrictedBackupMode, null)、installContentProviders(app,data.providers)和 mInstrumentation.callApplicationOnCreate(app),其中 installContentProviders(app,data.providers) 调用了 ContentProvider 的 onCreate 方法 , 而 mInstrumentation.callApplicationOnCreate(app) 调用了 Application 的 onCreate 方法,所以 ContentProvider 的 onCreate 方法比 Application 的 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();
    }

}

2、ContentProvider 源码解析-query 方法

ContentProvider 的实现类要实现的方法有 onCreate、getType、delete、update、query 和 insert 方法,这里我们只解析一下 query 方法的源码,对其他5个方法有兴趣的读者可以自己去阅读一下它们的源码;一开始是获取一个 ContentResolver 对象,是交给 ContextWrapper 的 getContentResolver 方法;

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

这时候 ContextWrapper 的 getContentResolver 方法是交给 ContextImpl 的 getContentResolver 方法来实现,mBase 就是 ContextImpl 类型的对象,ContextImpl 类的 getContentResolver 返回的是 mContentResolver,它是一个 ApplicationContentResolver 类型的对象;

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

到这里之后就可以开始分析 query 方法的源码了,先看一下 ContentResolver 中含有5个参数的 query 方法,该方法又调用了 ContentResolver 中含有6个参数的 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);

}

我们往下看 ContentResolver 中含有6个参数的 query 方法,发现该方法调用了 ContentResolver 中含有4个参数的 query 方法;
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);

}

再查看 ContentResolver 中含有4个参数的 query 方法,发现该方法调用了 ContentResolver 中含有1个参数的 acquireUnstableProvider 方法;
Cursor query(final @RequiresPermission.Read @NonNull Uri uri,

             @Nullable String[] projection, @Nullable Bundle queryArgs,
             @Nullable CancellationSignal cancellationSignal) {
    Preconditions.checkNotNull(uri, "uri");
    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    ......

}

往下看 ContentResolver 中含有1个参数的 acquireUnstableProvider 方法,发现该方法调用了 ContentResolver 中含有2个参数的 acquireUnstableProvider 方法;
public final IContentProvider acquireUnstableProvider(Uri uri) {

    if (!SCHEME_CONTENT.equals(uri.getScheme())) {
        return null;
    }
    String auth = uri.getAuthority();
    if (auth != null) {
        return acquireUnstableProvider(mContext, uri.getAuthority());
    }
    return null;

}

查看 ApplicationContentResolver(是 ContentResolver 类的子类,也是 ContextImpl 的内部类) 中含有2个参数的 acquireUnstableProvider 方法,发现调用了 ActivityThread 的 acquireProvider 方法,mMainThread 就是 ActivityThread;

    @Override
    protected IContentProvider acquireUnstableProvider(Context c, String auth) {
        return mMainThread.acquireProvider(c,
                ContentProvider.getAuthorityWithoutUserId(auth),
                resolveUserIdFromAuthority(auth), false);
    }

点击查看 ActivityThread 的 acquireProvider 方法,首先会从 ActivityThread 中查找是否已经存在目标 ContentProvider了,如果存在就直接返回
;如果没有,就调用 AMS 获取 ContentProvider 对象;
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.
    ContentProviderHolder holder = null;
    try {
        holder = ActivityManager.getService().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;

}

我们从客户端拿到的 ContentProvider 并不是原始的 ContentProvider,而是 ContentProvider 的 Binder 类型的对象IContentProvider,IContentProvider的具体实现是 ContentProviderNative 和 ContentProvider.Transport,其中 ContentProvider.Transport 继承了 ContentProviderNative,下面我们看 ContentProvider.Transport 的 query 方法,该方法又调用了 ContentProvider 的4个参数的 query 方法;

@Override
public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection,
                    @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) {
    ......
    try {
        return ContentProvider.this.query(
                uri, projection, queryArgs,
                CancellationSignal.fromTransport(cancellationSignal));
    } finally {
        setCallingPackage(original);
    }
}

我们再点击查看 ContentProvider 的4个参数的 query 方法,发现它调用了 ContentProvider 的6个参数的 query 方法;
public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,

        @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) {
    queryArgs = queryArgs != null ? queryArgs : Bundle.EMPTY;

    // if client doesn't supply an SQL sort order argument, attempt to build one from
    // QUERY_ARG_SORT* arguments.
    String sortClause = queryArgs.getString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER);
    if (sortClause == null && queryArgs.containsKey(ContentResolver.QUERY_ARG_SORT_COLUMNS)) {
        sortClause = ContentResolver.createSqlSortClause(queryArgs);
    }

    return query(
            uri,
            projection,
            queryArgs.getString(ContentResolver.QUERY_ARG_SQL_SELECTION),
            queryArgs.getStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS),
            sortClause,
            cancellationSignal);

}

查看 ContentProvider 的6个参数的 query 方法,发现它调用了 ContentProvider 的5个参数的 query 方法,这时候 5个参数的 query 方法是抽象方法,最终调用到了我们自己继承 ContentProvider 类的子类的 query 方法;
public @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,

        @Nullable String selection, @Nullable String[] selectionArgs,
        @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
    return query(uri, projection, selection, selectionArgs, sortOrder);

}

你可能感兴趣的:(Android中的ContentProvider源码解析)