Android 四大组件之ContentProvider工作原理

本文深入解析Android中ContentProvider的启动流程与工作原理,包括其如何在进程启动时初始化,与Application对象创建的先后顺序,以及跨进程调用机制。重点介绍了ContentProvider的onCreate方法执行时机,以及其与Application onCreate方法的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自: https://blog.youkuaiyun.com/tianmi1988/article/details/51077378

ContentProvider启动

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

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


 
  1. public static void main(String[] args) {
  2. SamplingProfilerIntegration.start();
  3. // CloseGuard defaults to true and can be quite spammy. We
  4. // disable it here, but selectively enable it later (via
  5. // StrictMode) on debug builds, but using DropBox, not logs.
  6. CloseGuard.setEnabled( false);
  7. Environment.initForCurrentUser();
  8. // Set the reporter for event logging in libcore
  9. EventLogger.setReporter( new EventLoggingReporter());
  10. Security.addProvider( new AndroidKeyStoreProvider());
  11. // Make sure TrustedCertificateStore looks in the right place for CA certificates
  12. final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
  13. TrustedCertificateStore.setDefaultUserDirectory(configDir);
  14. Process.setArgV0( "<pre-initialized>");
  15. Looper.prepareMainLooper();
  16. ActivityThread thread = new ActivityThread();
  17. thread.attach( false);
  18. if (sMainThreadHandler == null) {
  19. sMainThreadHandler = thread.getHandler();
  20. }
  21. AsyncTask.init();
  22. if ( false) {
  23. Looper.myLooper().setMessageLogging( new
  24. LogPrinter(Log.DEBUG, "ActivityThread"));
  25. }
  26. Looper.loop();
  27. throw new RuntimeException( "Main thread loop unexpectedly exited");
  28. }

主要看attach方法


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


 
  1. @Override
  2. public final void attachApplication(IApplicationThread thread) {
  3. synchronized ( this) {
  4. int callingPid = Binder.getCallingPid();
  5. final long origId = Binder.clearCallingIdentity();
  6. attachApplicationLocked(thread, callingPid);
  7. Binder.restoreCallingIdentity(origId);
  8. }
  9. }

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


 
  1. private final boolean attachApplicationLocked(IApplicationThread thread,
  2. int pid) {
  3. ...
  4. ProfilerInfo profilerInfo = profileFile == null ? null
  5. : new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop);
  6. thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
  7. profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
  8. app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
  9. isRestrictedBackupMode || !normalMode, app.persistent,
  10. new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),
  11. mCoreSettingsObserver.getCoreSettingsLocked());
  12. updateLruProcessLocked(app, false, null);
  13. ...
  14. }

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

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

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

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

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


 
  1. private void handleBindApplication(AppBindData data) {
  2. ...
  3. try {
  4. // If the app is being launched for full backup or restore, bring it up in
  5. // a restricted environment with the base application class.
  6. Application app = data.info.makeApplication(data.restrictedBackupMode, null);
  7. mInitialApplication = app;
  8. // don't bring up providers in restricted mode; they may depend on the
  9. // app's custom Application class
  10. if (!data.restrictedBackupMode) {
  11. List<ProviderInfo> providers = data.providers;
  12. if (providers != null) {
  13. installContentProviders(app, providers);
  14. // For process that contains content providers, we want to
  15. // ensure that the JIT is enabled "at some point".
  16. mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10* 1000);
  17. }
  18. }
  19. // Do this after providers, since instrumentation tests generally start their
  20. // test thread at this point, and we don't want that racing.
  21. try {
  22. mInstrumentation.onCreate(data.instrumentationArgs);
  23. }
  24. catch (Exception e) {
  25. throw new RuntimeException(
  26. "Exception thrown in onCreate() of "
  27. + data.instrumentationName + ": " + e.toString(), e);
  28. }
  29. try {
  30. mInstrumentation.callApplicationOnCreate(app);
  31. } catch (Exception e) {
  32. if (!mInstrumentation.onException(app, e)) {
  33. throw new RuntimeException(
  34. "Unable to create application " + app.getClass().getName()
  35. + ": " + e.toString(), e);
  36. }
  37. }
  38. } finally {
  39. StrictMode.setThreadPolicy(savedPolicy);
  40. }
  41. }

其中依次会调用到

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


 
  1. private void installContentProviders(
  2. Context context, List<ProviderInfo> providers) {
  3. final ArrayList<IActivityManager.ContentProviderHolder> results =
  4. new ArrayList<IActivityManager.ContentProviderHolder>();
  5. for (ProviderInfo cpi : providers) {
  6. if (DEBUG_PROVIDER) {
  7. StringBuilder buf = new StringBuilder( 128);
  8. buf.append( "Pub ");
  9. buf.append(cpi.authority);
  10. buf.append( ": ");
  11. buf.append(cpi.name);
  12. Log.i(TAG, buf.toString());
  13. }
  14. IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
  15. false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
  16. if (cph != null) {
  17. cph.noReleaseNeeded = true;
  18. results.add(cph);
  19. }
  20. }
  21. try {
  22. ActivityManagerNative.getDefault().publishContentProviders(
  23. getApplicationThread(), results);
  24. } catch (RemoteException ex) {
  25. }
  26. }

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


 
  1. private IActivityManager. ContentProviderHolder installProvider(Context context,
  2. IActivityManager.ContentProviderHolder holder, ProviderInfo info,
  3. boolean noisy, boolean noReleaseNeeded, boolean stable) {
  4. ...
  5. try {
  6. final java.lang.ClassLoader cl = c.getClassLoader();
  7. localProvider = (ContentProvider)cl.
  8. loadClass(info.name).newInstance();
  9. provider = localProvider.getIContentProvider();
  10. if (provider == null) {
  11. Slog.e(TAG, "Failed to instantiate class " +
  12. info.name + " from sourceDir " +
  13. info.applicationInfo.sourceDir);
  14. return null;
  15. }
  16. if (DEBUG_PROVIDER) Slog.v(
  17. TAG, "Instantiating local provider " + info.name);
  18. // XXX Need to create the correct context for this provider.
  19. localProvider.attachInfo(c, info);
  20. }
  21. ...
  22. }
仿佛看到曙光了,ContentProvider.attachInfo


 
  1. private void attachInfo(Context context, ProviderInfo info, boolean testing) {
  2. /*
  3. * We may be using AsyncTask from binder threads. Make it init here
  4. * so its static handler is on the main thread.
  5. */
  6. AsyncTask.init();
  7. mNoPerms = testing;
  8. /*
  9. * Only allow it to be set once, so after the content service gives
  10. * this to us clients can't change it.
  11. */
  12. if (mContext == null) {
  13. mContext = context;
  14. if (context != null) {
  15. mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
  16. Context.APP_OPS_SERVICE);
  17. }
  18. mMyUid = Process.myUid();
  19. if (info != null) {
  20. setReadPermission(info.readPermission);
  21. setWritePermission(info.writePermission);
  22. setPathPermissions(info.pathPermissions);
  23. mExported = info.exported;
  24. mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
  25. setAuthorities(info.authority);
  26. }
  27. ContentProvider. this.onCreate();
  28. }
  29. }
最后一行代码验证了我们前面的结论, 虽然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中


 
  1. private static final class ApplicationContentResolver extends ContentResolver {
  2. private final ActivityThread mMainThread;
  3. private final UserHandle mUser;
  4. public ApplicationContentResolver(
  5. Context context, ActivityThread mainThread, UserHandle user) {
  6. super(context);
  7. mMainThread = Preconditions.checkNotNull(mainThread);
  8. mUser = Preconditions.checkNotNull(user);
  9. }
  10. @Override
  11. protected IContentProvider acquireProvider(Context context, String auth) {
  12. return mMainThread.acquireProvider(context,
  13. ContentProvider.getAuthorityWithoutUserId(auth),
  14. resolveUserIdFromAuthority(auth), true);
  15. }
  16. @Override
  17. protected IContentProvider acquireExistingProvider(Context context, String auth) {
  18. return mMainThread.acquireExistingProvider(context,
  19. ContentProvider.getAuthorityWithoutUserId(auth),
  20. resolveUserIdFromAuthority(auth), true);
  21. }
  22. @Override
  23. public boolean releaseProvider(IContentProvider provider) {
  24. return mMainThread.releaseProvider(provider, true);
  25. }
  26. @Override
  27. protected IContentProvider acquireUnstableProvider(Context c, String auth) {
  28. return mMainThread.acquireProvider(c,
  29. ContentProvider.getAuthorityWithoutUserId(auth),
  30. resolveUserIdFromAuthority(auth), false);
  31. }
  32. @Override
  33. public boolean releaseUnstableProvider(IContentProvider icp) {
  34. return mMainThread.releaseProvider(icp, false);
  35. }
  36. @Override
  37. public void unstableProviderDied(IContentProvider icp) {
  38. mMainThread.handleUnstableProviderDied(icp.asBinder(), true);
  39. }
  40. @Override
  41. public void appNotRespondingViaProvider(IContentProvider icp) {
  42. mMainThread.appNotRespondingViaProvider(icp.asBinder());
  43. }
  44. /** @hide */
  45. protected int resolveUserIdFromAuthority(String auth) {
  46. return ContentProvider.getUserIdFromAuthority(auth, mUser.getIdentifier());
  47. }
  48. }
看完了 getContentResolver.query的类,接着看方法的实现,方法实现在ApplicationContentResolver父类ContentResolver中


 
  1. public final Cursor query(final Uri uri, String[] projection,
  2. String selection, String[] selectionArgs, String sortOrder,
  3. CancellationSignal cancellationSignal) {
  4. IContentProvider unstableProvider = acquireUnstableProvider(uri);
  5. if (unstableProvider == null) {
  6. return null;
  7. }
  8. IContentProvider stableProvider = null;
  9. Cursor qCursor = null;
  10. try {
  11. long startTime = SystemClock.uptimeMillis();
  12. ICancellationSignal remoteCancellationSignal = null;
  13. if (cancellationSignal != null) {
  14. cancellationSignal.throwIfCanceled();
  15. remoteCancellationSignal = unstableProvider.createCancellationSignal();
  16. cancellationSignal.setRemote(remoteCancellationSignal);
  17. }
  18. try {
  19. qCursor = unstableProvider.query(mPackageName, uri, projection,
  20. selection, selectionArgs, sortOrder, remoteCancellationSignal);
  21. }
  22. ...
  23. ...
首先会获取到一个IContentProvider对象,IContentProvider unstableProvider = acquireUnstableProvider(uri);其本质是通过调用了 ApplicationContentResolver类自身的acquireProvider来获取的,

通过上面ApplicationContentResolver类的定义可以看出直接调用了ActivityThread的acquireProvider


 
  1. public final IContentProvider acquireProvider(
  2. Context c, String auth, int userId, boolean stable) {
  3. final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
  4. if (provider != null) {
  5. return provider;
  6. }
  7. // There is a possible race here. Another thread may try to acquire
  8. // the same provider at the same time. When this happens, we want to ensure
  9. // that the first one wins.
  10. // Note that we cannot hold the lock while acquiring and installing the
  11. // provider since it might take a long time to run and it could also potentially
  12. // be re-entrant in the case where the provider is in the same process.
  13. IActivityManager.ContentProviderHolder holder = null;
  14. try {
  15. holder = ActivityManagerNative.getDefault().getContentProvider(
  16. getApplicationThread(), auth, userId, stable);
  17. } catch (RemoteException ex) {
  18. }
  19. if (holder == null) {
  20. Slog.e(TAG, "Failed to find provider info for " + auth);
  21. return null;
  22. }
  23. // Install provider will increment the reference count for us, and break
  24. // any ties in the race.
  25. holder = installProvider(c, holder, holder.info,
  26. true /*noisy*/, holder.noReleaseNeeded, stable);
  27. return holder.provider;
  28. }

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


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

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

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


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


 
  1. private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
  2. String name, IBinder token, boolean stable, int userId) {
  3. ContentProviderRecord cpr;
  4. ...
  5. if (!providerRunning) {
  6. ...
  7. ProcessRecord proc = getProcessRecordLocked(
  8. cpi.processName, cpr.appInfo.uid, false);
  9. if (proc != null && proc.thread != null) {
  10. if (DEBUG_PROVIDER) {
  11. Slog.d(TAG, "Installing in existing process " + proc);
  12. }
  13. checkTime(startTime, "getContentProviderImpl: scheduling install");
  14. proc.pubProviders.put(cpi.name, cpr);
  15. try {
  16. proc.thread.scheduleInstallProvider(cpi);
  17. } catch (RemoteException e) {
  18. }} else {
  19.                             checkTime(startTime, "getContentProviderImpl: before start process");
  20.                             proc = startProcessLocked(cpi.processName,
  21.                                     cpr.appInfo, false, 0, "content provider",
  22.                                     new ComponentName(cpi.applicationInfo.packageName,
  23.                                             cpi.name), false, false, false);
  24.                             checkTime(startTime, "getContentProviderImpl: after start process");
  25.                             if (proc == null) {
  26.                                 Slog.w(TAG, "Unable to launch app "
  27.                                         + cpi.applicationInfo.packageName + "/"
  28.                                         + cpi.applicationInfo.uid + " for provider "
  29.                                         + name + ": process is bad");
  30.                                 return null;
  31.                             }
  32.                         }
  33.                         cpr.launchingApp = proc;
  34.                         mLaunchingProviders.add(cpr);
  35. ...
  36. ...
  37. }

启动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


 
  1. class Transport extends ContentProviderNative {
  2. AppOpsManager mAppOpsManager = null;
  3. int mReadOp = AppOpsManager.OP_NONE;
  4. int mWriteOp = AppOpsManager.OP_NONE;
  5. ContentProvider getContentProvider() {
  6. return ContentProvider. this;
  7. }
  8. @Override
  9. public String getProviderName() {
  10. return getContentProvider().getClass().getName();
  11. }
  12. @Override
  13. public Cursor query(String callingPkg, Uri uri, String[] projection,
  14. String selection, String[] selectionArgs, String sortOrder,
  15. ICancellationSignal cancellationSignal) {
  16. validateIncomingUri(uri);
  17. uri = getUriWithoutUserId(uri);
  18. if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
  19. return rejectQuery(uri, projection, selection, selectionArgs, sortOrder,
  20. CancellationSignal.fromTransport(cancellationSignal));
  21. }
  22. final String original = setCallingPackage(callingPkg);
  23. try {
  24. return ContentProvider. this.query(
  25. uri, projection, selection, selectionArgs, sortOrder,
  26. CancellationSignal.fromTransport(cancellationSignal));
  27. } finally {
  28. setCallingPackage(original);
  29. }
  30. }
可以清楚的看到这ContentProvider调用了自己的query方法,结果再通过Binder返回给调用进程。至此其他应用调用ContentProvider提供方法的过程分析完毕。其他update、insert、delete流程类似。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值