ContentProvider是一种内容共享型组件,它通过Binder向其他组件乃至其他应用提供数据。当ContentProvider所在的进程启动时,ContentProvider会同事启动并被发布到AMS中。需要注意的是,这个时候ContentProvider的onCreate要先于Application的onCreate而执行,这是四大组件中一个少有的现象。

当一个应用启动时,入口方法为ActivityThread的main方法,main方法是一个静态方法,在main方法中会创建ActivityThread的实例并创建主线程的消息队列,然后再ActivityThread的attach方法中会远程调用AMS的attachApplication方法并将ApplicationThread对象提供给AMS。ApplicationThread是一个Binder对象,它的Binder接口是IApplicationThread,主要用于ActivityThread和AMS之间的通信。在AMS的attachApplication方法中,会调用ApplicationThread的bindApplication方法,bindApplicaiton的逻辑会经过ActivityThread中的Handler切换到ActivityThread中去执行,具体的方法是handleBindApplication,在这个方法中ActivityThread会创建Applicaiton对象并加载ContentProvider。需要注意的是ActivityThread会先加载ContentProvider,然后再调用Applicaiton的onCreate方法。
ContentProvider启动后,外界就可以通过它所提供的增删改查四个接口来操作数据源,即insert、delete、update、query。这四个方法都是通过Binder来调用的,外界无法直接访问ContentProvider,它只能通过AMS根据Uri来获取对应的ContentProvider的Binder接口IContentProvider,然后在通过IContentProvider来访问ContentProvider中的数据源。
一般来说ContentProvider都应该是单实例的。具体是由android:multiprocess属性来决定的,当android:multiprocess为false时,ContentProvider为单实例,这也是默认值,当multiprocess为true时,为多实例,这个时候在每个调用者的进程中都存在一个ContentProvider对象。由于在实际开发中,并未发现多实例的ContentProvider的具体使用场景,因此我们可以简单认为ContentProvider都是单实例的。

启动流程

访问ContentProvider需要通过ContentResolver,ContentResolver是一个抽象类,通过Context的getContentResolver方法获取的实际上是ApplicationContentResolver对象。ApplicationContentResolver类继承了ContentResolver并实现了ContentResolver中的抽象方法。当ContentProvider所在的进程未启动时,第一次访问它时就会触发ContentProvider的创建。通过ContentProvider的四个方法中的任何一个都可以触发ContentProvider的启动过程。这里选择query方法。

ContentResolver.java

@Overridepublic final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,@Nullable String[] projection, @Nullable Bundle queryArgs,@Nullable CancellationSignal cancellationSignal) {Objects.requireNonNull(uri, "uri");try {if (mWrapped != null) {return mWrapped.query(uri, projection, queryArgs, cancellationSignal);}} catch (RemoteException e) {return null;}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, mAttributionTag, uri, projection,queryArgs, 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!!!!1unstableProviderDied(unstableProvider);stableProvider = acquireProvider(uri);if (stableProvider == null) {return null;}qCursor = stableProvider.query(mPackageName, mAttributionTag, uri, projection,queryArgs, 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, queryArgs);// 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;} catch (RemoteException e) {// Arbitrary and not worth documenting, as Activity// Manager will kill this process shortly anyway.return null;} finally {if (qCursor != null) {qCursor.close();}if (cancellationSignal != null) {cancellationSignal.setRemote(null);}if (unstableProvider != null) {releaseUnstableProvider(unstableProvider);}if (stableProvider != null) {releaseProvider(stableProvider);}}}
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;
}

首先会通过acquireUnstableProvider——>acquireProvider方法获取ContentProvider。该方法是个抽象方法,具体实现在ApplicationContentResolver类中,是ContextImpl的一个静态内部类。

 private static final class ApplicationContentResolver extends ContentResolver {...@Override@UnsupportedAppUsageprotected IContentProvider acquireProvider(Context context, String auth) {return mMainThread.acquireProvider(context,ContentProvider.getAuthorityWithoutUserId(auth),resolveUserIdFromAuthority(auth), true);}...

ApplicationContentResolver的acquireProvider方法并没有处理任何逻辑,它直接调用了ActivityThread的acquireProvider方法。

ActivityThread.java

  public final IContentProvider acquireProvider(Context c, String auth, int userId, boolean stable) {//从缓存中获取ContentProvider实例对象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 {//当缓存中没有ContentProvider实例时吗,需要通过AMS来创建一个ContentProvider实例synchronized (getGetProviderLock(auth, userId)) {holder = ActivityManager.getService().getContentProvider(getApplicationThread(), c.getOpPackageName(), auth, userId, stable);}} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}if (holder == null) {if (UserManager.get(c).isUserUnlocked(userId)) {Slog.e(TAG, "Failed to find provider info for " + auth);} else {Slog.w(TAG, "Failed to find provider info for " + auth + " (user not unlocked)");}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,如果存在就直接返回。ActivityThread通过mProviderMap来存储已经启动的ContentProvider对象。如果ContentProvider没有启动,那么就发送一个进程间请求给AMS让其启动目标ContentProvider,最后再通过installProvider方法来修改引用计数。
ContentProvider被启动时会伴随着进程的启动,在AMS中,首先会启动ContentProvider所在的进程,然后在启动ContentProvider。启动进程是由AMS的startProcessLocked方法来完成的,其内部主要是通过Process的start方法来向Zygote申请fork一个新进程的启动,新进程启动后会反射调用ActivityThread的main方法。

public static void main(String[] args) {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");// Install selective syscall interceptionAndroidOs.install();// 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();// Make sure TrustedCertificateStore looks in the right place for CA certificatesfinal File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());TrustedCertificateStore.setDefaultUserDirectory(configDir);// Call per-process mainline module initialization.initializeMainlineModules();Process.setArgV0("<pre-initialized>");Looper.prepareMainLooper();// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.// It will be in the format "seq=114"long startSeq = 0;if (args != null) {for (int i = args.length - 1; i >= 0; --i) {if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {startSeq = Long.parseLong(args[i].substring(PROC_START_SEQ_IDENT.length()));}}}ActivityThread thread = new ActivityThread();thread.attach(false, startSeq);if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}if (false) {Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG, "ActivityThread"));}// End of event ActivityThreadMain.Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");}

main方法是个静态方法,首先会创建ActivityThread实例并调用attch来进行一系列的初始化,接着就开始进行消息循环了。ActivityThread的attach方法会将ApplicationThread对象通过AMS的attachApplication方法跨进程传递给AMS,最终AMS会完成ContentProvider的创建。

ActivityThread.java

@UnsupportedAppUsage
private void attach(boolean system, long startSeq) {sCurrentActivityThread = this;mSystemThread = system;if (!system) {android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",UserHandle.myUserId());RuntimeInit.setApplicationObject(mAppThread.asBinder());final IActivityManager mgr = ActivityManager.getService();try {//在AMS中注册ApplicationThreadmgr.attachApplication(mAppThread, startSeq);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}// Watch for getting close to heap limit.BinderInternal.addGcWatcher(new Runnable() {@Override public void run() {if (!mSomeActivitiesChanged) {return;}Runtime runtime = Runtime.getRuntime();long dalvikMax = runtime.maxMemory();long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();if (dalvikUsed > ((3*dalvikMax)/4)) {if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024)+ " total=" + (runtime.totalMemory()/1024)+ " used=" + (dalvikUsed/1024));mSomeActivitiesChanged = false;try {ActivityTaskManager.getService().releaseSomeActivities(mAppThread);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}}});} else {// Don't set application object here -- if the system crashes,// we can't display an alert, we just want to die die die.android.ddm.DdmHandleAppName.setAppName("system_process",UserHandle.myUserId());try {mInstrumentation = new Instrumentation();mInstrumentation.basicInit(this);ContextImpl context = ContextImpl.createAppContext(this, getSystemContext().mPackageInfo);mInitialApplication = context.mPackageInfo.makeApplication(true, null);mInitialApplication.onCreate();} catch (Exception e) {throw new RuntimeException("Unable to instantiate Application():" + e.toString(), e);}}ViewRootImpl.ConfigChangedCallback configChangedCallback= (Configuration globalConfig) -> {synchronized (mResourcesManager) {// TODO (b/135719017): Temporary log for debugging IME service.if (Build.IS_DEBUGGABLE && mHasImeComponent) {Log.d(TAG, "ViewRootImpl.ConfigChangedCallback for IME, "+ "config=" + globalConfig);}// We need to apply this change to the resources immediately, because upon returning// the view hierarchy will be informed about it.if (mResourcesManager.applyConfigurationToResourcesLocked(globalConfig,null /* compat */)) {updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),mResourcesManager.getConfiguration().getLocales());// This actually changed the resources! Tell everyone about it.if (mPendingConfiguration == null|| mPendingConfiguration.isOtherSeqNewer(globalConfig)) {mPendingConfiguration = globalConfig;sendMessage(H.CONFIGURATION_CHANGED, globalConfig);}}}};ViewRootImpl.addConfigCallback(configChangedCallback);
}

AMS的attachApplication方法调用了attachApplicationLocked方法,attachApplicationLocked中又调用了ApplicationThread的bindApplication。

thread.bindApplication(processName, appInfo, providerList,instr2.mClass,profilerInfo, instr2.mArguments,instr2.mWatcher,instr2.mUiAutomationConnection, testMode,mBinderTransactionTrackingEnabled, enableTrackAllocation,isRestrictedBackupMode || !normalMode, app.isPersistent(),new Configuration(app.getWindowProcessController().getConfiguration()),app.compat, getCommonServicesLocked(app.isolated),mCoreSettingsObserver.getCoreSettingsLocked(),buildSerial, autofillOptions, contentCaptureOptions,app.mDisabledCompatChanges);
@Override
public final void bindApplication(String processName, ApplicationInfo appInfo,ProviderInfoList providerList, 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, AutofillOptions autofillOptions,ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges) {if (services != null) {if (false) {// Test code to make sure the app could see the passed-in services.for (Object oname : services.keySet()) {if (services.get(oname) == null) {continue; // AM just passed in a null service.}String name = (String) oname;// See b/79378449 about the following exemption.switch (name) {case "package":case Context.WINDOW_SERVICE:continue;}if (ServiceManager.getService(name) == null) {Log.wtf(TAG, "Service " + name + " should be accessible by this app");}}}// Setup the service cache in the ServiceManagerServiceManager.initServiceCache(services);}setCoreSettings(coreSettings);AppBindData data = new AppBindData();data.processName = processName;data.appInfo = appInfo;data.providers = providerList.getList();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;data.autofillOptions = autofillOptions;data.contentCaptureOptions = contentCaptureOptions;data.disabledCompatChanges = disabledCompatChanges;sendMessage(H.BIND_APPLICATION, data);
}

ActivityThread的bindApplicaiton会发送一个BIND_APPLICATION类型的消息给handler H,它收到消息后会调用ActivityThread的handleBindActivity方法,完成了Application的创建以及ContentProvider的创建。

  1. 创建ContextImpl和Instrumentation
  2. 创建Application对象
  3. 启动当前进程的ContentProvider并调用其onCreate方法
  4. 调用Applicaiton的onCreate方法

ContentProvider流程详解相关推荐

  1. 跨境电商三单对碰三单申报流程详解

    跨境电商三单对碰三单申报流程详解 概要:三单申报是指"电子订单.电子运单.支付凭证". 1.电子订单: 适合申报企业类型"电商企业.电商交易平台.电商境内代理企业&quo ...

  2. Android事件流程详解

    Android事件流程详解 网络上有不少博客讲述了android的事件分发机制和处理流程机制,但是看过千遍,总还是觉得有些迷迷糊糊,因此特地抽出一天事件来亲测下,向像我一样的广大入门程序员详细讲述an ...

  3. 基于spark mllib_Spark高级分析指南 | 机器学习和分析流程详解(下)

    - 点击上方"中国统计网"订阅我吧!- 我们在Spark高级分析指南 | 机器学习和分析流程详解(上)快速介绍了一下不同的高级分析应用和用力,从推荐到回归.但这只是实际高级分析过程 ...

  4. View的绘制-draw流程详解

    目录 作用 根据 measure 测量出的宽高,layout 布局的位置,渲染整个 View 树,将界面呈现出来. 具体分析 以下源码基于版本27 DecorView 的draw 流程 在<Vi ...

  5. 杂志订阅管理系统c++_电池管理系统BMS功能安全开发流程详解

    点击上面 "电动知家"可以订阅哦! BMS功能安全开发流程详解 BMS和ISO26262 - BMS & ISO26262简介 BMS即Battery Management ...

  6. View的绘制-layout流程详解

    目录 作用 根据 measure 测量出来的宽高,确定所有 View 的位置. 具体分析 View 本身的位置是通过它的四个点来控制的: 以下涉及到源码的部分都是版本27的,为方便理解观看,代码有所删 ...

  7. U-Boot启动流程详解

    参考:U-Boot顶层目录链接脚本文件(u-boot.lds)介绍 作者:一只青木呀 发布时间: 2020-10-23 13:52:23 网址:https://blog.csdn.net/weixin ...

  8. java isight zmf_isight集成catia和abaqus,nastran流程详解

    isight集成catia和abaqus,nastran流程详解 CAD软件中参数化建模,导入有限元软件中计算各个工况,isight根据计算结果调整模型参数,反复迭代计算的过程是尺寸优化的典型问题~ ...

  9. java处理请求的流程_Java Spring mvc请求处理流程详解

    Spring mvc请求处理流程详解 前言 spring mvc框架相信很多人都很熟悉了,关于这方面的资料也是一搜一大把.但是感觉讲的都不是很细致,让很多初学者都云里雾里的.本人也是这样,之前研究过, ...

最新文章

  1. spring aop设计模式_Spring框架中设计模式的运用
  2. git 原理详解及实用指南_如何编写良好的提交消息:实用的Git指南
  3. 请写出至少五个块级元素_Java 面试题(五)
  4. MySQL回放_mysql回顾
  5. Java异常:选择Checked Exception还是Unchecked Exception?
  6. Spring Data JPA 条件查询 分页查询
  7. 程序员面试难题,在你结婚的时候领导要求你30分钟归队,你会如何
  8. Unity3D的音效相关介绍
  9. 计算机专业英语词汇pdf,计算机专业英语词汇(完美排版,大容量打印版).pdf.pdf
  10. treetable怎么带参数_jquery treeTable插件使用细则
  11. python Excel公式
  12. Sphinx入门教程
  13. 百度AI文字识别需要注意的点
  14. C#中使用S7.net与西门子PLC通讯
  15. 杭电c语言课程设计短学期第七次作业,杭电短学期数字电子钟整点报时系统实验报告...
  16. KMP算法求循环节,为什么能求循环节
  17. LiteOS 软件定时器
  18. 哎,又跟HR在小群吵了一架!
  19. python中获得字符串s长度的方法是什么_获得字符串s长度的方法是什么?【 】(8.0分)_学小易找答案...
  20. winamp 珍藏_Winamp 6可能不是汽配并且可能在2019年到货(可能)

热门文章

  1. php json_encode不转义,php json_encode不转义中文汉字的方法
  2. Spring中StopWatch的使用
  3. 漫画:如何给女朋友解释什么是反向代理?
  4. 猿创征文|【第11题】求坐上公交的最晚时间(考察贪心算法)
  5. 英特尔酷睿 i9-11900K 首发评测优势分析,新架构Cypress Cove较上代性能提高19%
  6. Java获取当前系统的年份
  7. android浏览器中 MUI 点击事件不触发的解决方法
  8. 数据库 -> 索引的基本原理
  9. 3xian 退役帖------acm精神世界的捍卫者
  10. 2019某行业CTF大赛题目复现——图片隐写