Android 四大组件之ContentProvider工作原理

来源:互联网 发布:软件测试 培训课程 编辑:程序博客网 时间:2024/06/06 02:13

ContentProvider启动

ContentProvider是一种内容共享型组件,实际上它是通过Binder向其它应用提供数据。当ContentProvider所在的进程启动时,ContentProvider会同时启动并被发布到AMS中,需要特别注意的是ContentProvider的onCreate方法要早于Application的onCreate方法执行。

废话不多说先看源码,As we all known,每个进程的入口都是ActivityThread.main

public static void main(String[] args) {        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());        Security.addProvider(new AndroidKeyStoreProvider());        // 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();        }        AsyncTask.init();        if (false) {            Looper.myLooper().setMessageLogging(new                    LogPrinter(Log.DEBUG, "ActivityThread"));        }        Looper.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }

主要看attach方法

private void attach(boolean system) {  ... final IActivityManager mgr = ActivityManagerNative.getDefault();            try {                mgr.attachApplication(mAppThread);            } catch (RemoteException ex) {                // Ignore            }...}
通过前面的分析,这里一眼就可以看出IPC通信调用AMS的同名方法,接着往下看ActivityManagerService.java

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

AMS.attachApplicationLocked这个方法也是很长,这里截取最关键的

private final boolean attachApplicationLocked(IApplicationThread thread,            int pid) {   ... ProfilerInfo profilerInfo = profileFile == null ? null                    : new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop);            thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,                    profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,                    app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,                    isRestrictedBackupMode || !normalMode, app.persistent,                    new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),                    mCoreSettingsObserver.getCoreSettingsLocked());            updateLruProcessLocked(app, false, null);   ...}

这里又IPC通信,返回客户端处理去了
总结一下,跟ContentProvider相关的事件

1.创建ActivityThread实例,并创建消息队列

2.ActivityThread的attach方法远程调用AMS的attachApplication方法,并将ApplicationThread对象传递给AMS。

3.AMS的attachApplication方法中会调用ApplicationThread的bingApplication(IPC调用)进而通过H对象切换到ActivityThread中执行handleBindApplication方法

这个方法有200多行,这里截取了最后比较关键的部分

 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) {                List<ProviderInfo> providers = data.providers;                if (providers != null) {                    installContentProviders(app, 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);            }            try {                mInstrumentation.callApplicationOnCreate(app);            } catch (Exception e) {                if (!mInstrumentation.onException(app, e)) {                    throw new RuntimeException(                        "Unable to create application " + app.getClass().getName()                        + ": " + e.toString(), e);                }            }        } finally {            StrictMode.setThreadPolicy(savedPolicy);        }}

其中依次会调用到

Application app = data.info.makeApplication(data.restrictedBackupMode, null);和installContentProviders(app, providers);方法,前者就是创建Application对象,而后者则是创建ContentProvider,创建ContentProvider之后则调用Application的onCreate方法。

 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) {        }    }

创建好后通过IPC通信,发布到AMS中,这里还没完接着往下挖,ActivityThread.installProvider 

private IActivityManager.ContentProviderHolder installProvider(Context context,            IActivityManager.ContentProviderHolder holder, ProviderInfo info,            boolean noisy, boolean noReleaseNeeded, boolean stable) {   ...   try {                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);            }  ...}
仿佛看到曙光了,ContentProvider.attachInfo

private void attachInfo(Context context, ProviderInfo info, boolean testing) {        /*         * We may be using AsyncTask from binder threads.  Make it init here         * so its static handler is on the main thread.         */        AsyncTask.init();        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();        }    }
最后一行代码验证了我们前面的结论,虽然Application创建在前,但ContentProvider的onCreate方法调用却在Application的onCreate之前.当ContentProvider所在的进程启动的时候,它会同时被启动并被发布到AMS中,这个时候它的onCreate要先去Application的onCreate执行。

这部分的逻辑,我在书上看到的一幅图我觉得无可挑剔,直接拍下来放在博客上




ContentProvider的启动过程:


1.当一个应用启动时,入口方法是ActivityThread的main方法,其中创建ActivityThread的实例并创建主线程的消息队列;
2.ActivityThread的attach方法中会远程调用ActivityManagerService的attachApplication,并将ApplicationThread提供给AMS,ApplicationThread主要用于ActivityThread和AMS之间的通信;
3.ActivityManagerService的attachApplication会调用ApplicationThread的bindApplication方法,这个方法会通过H切换到ActivityThread中去执行,即调用handleBindApplication方法;
4.handleBindApplication方法会创建Application对象并加载ContentProvider,注意是先加载ContentProvider,然后调用Application的onCreate方法。
(3)ContentProvider的android:multiprocess属性决定它是否是单实例,默认值是false,也就是默认是单实例。当设置为true时,每个调用者的进程中都存在一个ContentProvider对象。实际使用中并未发现这样的使用场景,所以一般我们可以认为其为单实例的。


ContentProvider工作原理

当调用ContentProvider的insert、delete、update、query方法中的任何一个时,如果ContentProvider所在的进程没有启动的话,那么就会触发ContentProvider的创建,并伴随着ContentProvider所在进程的启动。

这里直接从query方法顺藤摸瓜,我们的一般使用就是在Context中使用getContentResolver.query方法来获取数据

这里的Context.getContentResolver获取的是ApplicationContentResolver对象,它是ContextImpl的内部类,继承了ContentResolver并实现了其抽象方法。

代码在ContextImpl.java中

 private static final class ApplicationContentResolver extends ContentResolver {        private final ActivityThread mMainThread;        private final UserHandle mUser;        public ApplicationContentResolver(                Context context, ActivityThread mainThread, UserHandle user) {            super(context);            mMainThread = Preconditions.checkNotNull(mainThread);            mUser = Preconditions.checkNotNull(user);        }        @Override        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, mUser.getIdentifier());        }    }
看完了getContentResolver.query的类,接着看方法的实现,方法实现在ApplicationContentResolver父类ContentResolver中

 public final Cursor query(final Uri uri, String[] projection,            String selection, String[] selectionArgs, String sortOrder,            CancellationSignal cancellationSignal) {        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,                        selection, selectionArgs, sortOrder, remoteCancellationSignal);            }......
首先会获取到一个IContentProvider对象,IContentProvider unstableProvider = acquireUnstableProvider(uri);其本质是通过调用了ApplicationContentResolver类自身的acquireProvider来获取的,

通过上面ApplicationContentResolver类的定义可以看出直接调用了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) {        }        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;    }

以上代码首先会在ActivityThread中查找是否已经存在目标ContentProvider,如果有则直接返回。成员变量mProviderMap就是用来存储ContentProvider的

  final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap        = new ArrayMap<ProviderKey, ProviderClientRecord>();
当然不存在目标ContentProvider,则通过IPC调用给AMS,让AMS来启动目标ContentProvider进程并启动ContentProvider,再通过installProvider来修改引用计数。

ActivityManagerNative.getDefault().getContentProvider( getApplicationThread(), auth, userId, stable);

接下来重点关注,AMS怎样启动ContentProvider所在进程的,沿着IPC调用往下挖 AMS.getContentProvider

 @Override    public final ContentProviderHolder getContentProvider(            IApplicationThread caller, String name, int userId, boolean stable) {        enforceNotIsolatedCaller("getContentProvider");        if (caller == null) {            String msg = "null IApplicationThread when getting content provider "                    + name;            Slog.w(TAG, msg);            throw new SecurityException(msg);        }        // The incoming user check is now handled in checkContentProviderPermissionLocked() to deal        // with cross-user grant.        return getContentProviderImpl(caller, name, null, stable, userId);    }
getContentProviderImpl也是一个长函数,截取其中重要的部分

 private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,            String name, IBinder token, boolean stable, int userId) {        ContentProviderRecord cpr;  ...  if (!providerRunning) {    ...    ProcessRecord proc = getProcessRecordLocked(                                cpi.processName, cpr.appInfo.uid, false);                        if (proc != null && proc.thread != null) {                            if (DEBUG_PROVIDER) {                                Slog.d(TAG, "Installing in existing process " + proc);                            }                            checkTime(startTime, "getContentProviderImpl: scheduling install");                            proc.pubProviders.put(cpi.name, cpr);                            try {                                proc.thread.scheduleInstallProvider(cpi);                            } catch (RemoteException e) {                            }} else {                            checkTime(startTime, "getContentProviderImpl: before start process");                            proc = startProcessLocked(cpi.processName,                                    cpr.appInfo, false, 0, "content provider",                                    new ComponentName(cpi.applicationInfo.packageName,                                            cpi.name), false, false, false);                            checkTime(startTime, "getContentProviderImpl: after start process");                            if (proc == null) {                                Slog.w(TAG, "Unable to launch app "                                        + cpi.applicationInfo.packageName + "/"                                        + cpi.applicationInfo.uid + " for provider "                                        + name + ": process is bad");                                return null;                            }                        }                        cpr.launchingApp = proc;                        mLaunchingProviders.add(cpr);    ...  ...}

启动ContentProvider所在进程的就是AMS.startProcessLocked,其内部主要是通过Process的start方法来完成新进程的启动的,对这一部分好奇的读者可以移步到老罗的博客:Android应用程序进程启动过程的源代码分析。新进程启动后入口就是ActivityThread.main方法,这跟前面ContentProvider启动部分一致了。

就此总结一下前面ActivityThread.handleBindApplication方法

1.创建ContextImpl和Instrumentation

2.创建Application对象

3.启动ContentProvider所在进程并调用OnCreate方法

4.调用Application的OnCreate方法

到这里ContentProvider所在进程已经启动成功,其他应用也就可以通过它提供的接口方法来访问它了。需要注意的是其他应用拿到的并不是ContentProvider对象,而是IContentProvider对象,了解过AIDL的童鞋都知道这里的IContentProvider就是ContentProvider的Binder对象,IContentProvider的具体实现是ContentProviderNative和ContentProvider.Transport,而其中ContentProvider.Transport继承了ContentProviderNative。我们可以从AMN.getDefault()可以找到类似的设计。

其他应用通过AMS获取到ContentProvider的Binder引用IContentProvider,接着通过IPC调用执行ContentProvider所在进程方法

ContentProvider.Transport

class Transport extends ContentProviderNative {        AppOpsManager mAppOpsManager = null;        int mReadOp = AppOpsManager.OP_NONE;        int mWriteOp = AppOpsManager.OP_NONE;        ContentProvider getContentProvider() {            return ContentProvider.this;        }        @Override        public String getProviderName() {            return getContentProvider().getClass().getName();        }        @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) != AppOpsManager.MODE_ALLOWED) {                return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,                        CancellationSignal.fromTransport(cancellationSignal));            }            final String original = setCallingPackage(callingPkg);            try {                return ContentProvider.this.query(                        uri, projection, selection, selectionArgs, sortOrder,                        CancellationSignal.fromTransport(cancellationSignal));            } finally {                setCallingPackage(original);            }        }
可以清楚的看到这ContentProvider调用了自己的query方法,结果再通过Binder返回给调用进程。至此其他应用调用ContentProvider提供方法的过程分析完毕。其他update、insert、delete流程类似。



2 0