乐趣区

关于java: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<IActivityManager> IActivityManagerSingleton =
        new Singleton<IActivityManager>() {
            @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<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) {
    ......
    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<ProviderInfo> 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);

}

退出移动版