Android进阶 四大组件的工作过程(四):ContentProvider的工作过程

news/2024/11/29 2:49:11/

Android进阶 四大组件的工作工程(四):ContentProvider的工作过程

在这里插入图片描述

导语

本篇是介绍四大组件的最后一篇文章,前三篇文章里我们已经介绍了Activity,Service以及Broadcast的工作流程,那么这篇文章我们就来介绍内容提供器ContentProvider的工作流程。

前几篇文章:

  1. Android进阶 四大组件的工作过程(一):Activity的工作过程
  2. Android进阶 四大组件的工作过程(二):Service的工作过程
  3. Android进阶 四大组件的工作过程(三):广播的注册,发送和接收过程

ContextImpl到AMS的调用

内容提供者ContentProvider一般是用于跨进程间的通信的,目前我使用的话一般是会配合Room数据库进行使用。

一般来说我们在使用ContentProvider是这样的:

binding.btAdd.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String name = binding.edName.getText().toString();Book book = new Book(Uid,name);User user = new User(Uid,name,true);Uid++;ContentValues values = new ContentValues();values.put("book_id",book.uid);values.put("book_name",book.name);values.put("user_id",user.uid);values.put("user_name",user.name);values.put("user_isMale",user.isMaile);getContentResolver().insert(BookProvider.USER_CONTENT_URI,values);getContentResolver().insert(BookProvider.BOOK_CONTENT_URI,values);}});

可以看到我们需要先调用getContentResolver来获取内容解析器,然后用内容解析器来插入数据,那我们就从这里入手,看getContentResolver方法:

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

可以看到这里还是和之前的三大组件一样,会调用到ContentWrapper的ContextImpl的getContentResolver方法,所以我们接下来看ContextImpl的getContentResolver方法:

	private final ApplicationContentResolver mContentResolver;.......public ContentResolver getContentResolver() {return mContentResolver;}

可以看到,这里是会返回ContextImpl的成员变量,类型为ApplicationContentResolver,这个类型也是ContextImpl的内部类,它继承于ContentResolver内容解析器:

 private static final class ApplicationContentResolver extends ContentResolver 

那最后回到方法的调用,也就是说我们最后调用insert或者query等方法是会调用ApplicationContentResolver的对应方法的,这里我们以query方法为例,先来看它的query,是在
它的父类ContentResolver中实现的:

public 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) { //1---------1return mWrapped.query(uri, projection, queryArgs, cancellationSignal);}} catch (RemoteException e) {return null;}IContentProvider unstableProvider = acquireUnstableProvider(uri);//2------2if (unstableProvider == null) {return null;}IContentProvider stableProvider = null;Cursor qCursor = null;try {......try {qCursor = unstableProvider.query(mContext.getAttributionSource(), uri, projection,queryArgs, remoteCancellationSignal);//3----------3} 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(mContext.getAttributionSource(), uri, projection,queryArgs, remoteCancellationSignal);}.............}

这里在注释一处会先判断是否有装饰类,这个装饰类实际上是我们可以自定义创建的,它是在ContentProvider的构造方法中被初始化的,这里我们一般也不传自定义的装饰类;所以接下来就会来到注释二处,这里调用了acquireUnstableProvider来获得内容提供者对象;然后在注释三处调用了获得的内容提供者的query方法;

我们先来看注释二处它是怎么获得内容提供者的,这里会最终调用到ApplicationContentResolver的acquireUnstableProvider方法:

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

这里调用到了自身对应进程的ActivityThread的acquireProvider方法:

public final IContentProvider acquireProvider(Context c, String auth, int userId, boolean stable) {final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);//1----------1if (provider != null) { return provider;}ContentProviderHolder holder = null;final ProviderKey key = getGetProviderKey(auth, userId);try {synchronized (key) {holder = ActivityManager.getService().getContentProvider(//2------2getApplicationThread(), c.getOpPackageName(), auth, userId, stable);if (holder != null && holder.provider == null && !holder.mLocal) {synchronized (key.mLock) {if (key.mHolder != null) {if (DEBUG_PROVIDER) {Slog.i(TAG, "already received provider: " + auth);}} else {                            key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);}holder = key.mHolder;}if (holder != null && holder.provider == null) {// probably timed outholder = null;}}}}.........holder = installProvider(c, holder, holder.info,true /*noisy*/, holder.noReleaseNeeded, stable);//3------3return holder.provider;}

这里在注释一处会调用自身ActivityThread的acquireExistingProvider方法,从自身的mProviderMap成员变量中查找有没有目标ContentProvider,这个mProviderMap变量是一个ArrayMap,键为ProviderKey,值为ProviderClientRecord,显然是用于存储(缓存)ContentProvider的一个容器。

如果没有找到目标的ContentProvider,接下来就会调用注释二处的AMS的getContentProvider方法来获取holder,然后注释三处调用installProvider来安装Provider,最后返回holder的provider。

那么接下来就跳转到了注释二处AMS的getContentProvider方法,进入第二阶段。

AMS到ContentProvider的启动

上一节说到了现在跳转到了AMS的getContentProvider方法,我们先来看这个方法:

    public final ContentProviderHolder getContentProvider(IApplicationThread caller, String callingPackage, String name, int userId,boolean stable) {traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "getContentProvider: ", name);try {return mCpHelper.getContentProvider(caller, callingPackage, name, userId, stable);} finally {Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);}}

这里调转到了中间帮助类的getContentProvider方法:

ContentProviderHolder getContentProvider(IApplicationThread caller, String callingPackage,String name, int userId, boolean stable) {mService.enforceNotIsolatedCaller("getContentProvider");..............final int callingUid = Binder.getCallingUid();..............return getContentProviderImpl(caller, name, null, callingUid, callingPackage,null, stable, userId);}

这里继续跳转到了ContentProviderHolder的getContentProviderImpl方法:

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,String name, IBinder token, int callingUid, String callingPackage, String callingTag,boolean stable, int userId) {............synchronized (this) {if (!mSystemProvidersInstalled && cpi.applicationInfo.isSystemApp()&& "system".equals(cpi.processName)) {throw new IllegalStateException("Cannot access system provider: '"+ cpi.authority + "' before system providers are installed!");}}...........ProcessRecord proc = mService.getProcessRecordLocked(cpi.processName, cpr.appInfo.uid);//1-------1IApplicationThread thread;if (proc != null && (thread = proc.getThread()) != null&& !proc.isKilled()) {if (ActivityManagerDebugConfig.DEBUG_PROVIDER) {Slog.d(TAG, "Installing in existing process " + proc);}final ProcessProviderRecord pr = proc.mProviders;if (!pr.hasProvider(cpi.name)) {checkTime(startTime, "getContentProviderImpl: scheduling install");pr.installProvider(cpi.name, cpr);//2------2try {thread.scheduleInstallProvider(cpi);//3------3} catch (RemoteException e) {}}} else {checkTime(startTime, "getContentProviderImpl: before start process");proc = mService.startProcessLocked(//4-------4cpi.processName, cpr.appInfo, false, 0,new HostingRecord(HostingRecord.HOSTING_TYPE_CONTENT_PROVIDER,new ComponentName(cpi.applicationInfo.packageName, cpi.name)),Process.ZYGOTE_POLICY_FLAG_EMPTY, 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);} finally {Binder.restoreCallingIdentity(origId);}}...........}return cpr.newHolder(conn, false);}

这个方法比较长,我们就只截取它比较重要的部分。在注释一处,获得了内容调用者的指定进程,如果这个进程存在且应用程序线程对象thread不为null,并且进程记录对象proc没有被杀死(isKilled()返回false),则会调用注释二处的代码安装Provider并且调用注释三的ActivityThread的scheduleInstallProvider方法;如果不存在的话则会调用注释四处的startProcessLocked方法启动所需要的进程,这个过程在应用进程的启动过程中介绍过了,不过这里还是假设应用程序进程不存在,我们接下来看注释四处的方法,它最终会调用到ActivityThread的main方法,具体可以看我的这一篇文章:

Android应用程序进程的启动过程

这里也贴出流程图:
在这里插入图片描述
总之最后调用到了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();//1----1long 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();//2----2thread.attach(false, startSeq);//3----3if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}.........Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");}

这里在注释一处会先创建一个消息队列管理者Looper,然后在注释二处创建ActivityThread对象,最后在注释三处调用attach方法进行一些参数的配置,我们主要就来看这个attach方法:

    private void attach(boolean system, long startSeq) {.........mSystemThread = system;if (!system) {...........final IActivityManager mgr = ActivityManager.getService();//1------1try {mgr.attachApplication(mAppThread, startSeq);//2------2} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}// Watch for getting close to heap limit.BinderInternal.addGcWatcher(new Runnable() {@Override public void run() {if (!mSomeActivitiesChanged) {return;}.........if (dalvikUsed > ((3*dalvikMax)/4)) {...........} else {...........try {mInstrumentation = new Instrumentation();mInstrumentation.basicInit(this);ContextImpl context = ContextImpl.createAppContext(this, getSystemContext().mPackageInfo);//3------3mInitialApplication = context.mPackageInfo.makeApplicationInner(true, null);//4-------4mInitialApplication.onCreate();} catch (Exception e) {throw new RuntimeException("Unable to instantiate Application():" + e.toString(), e);}}...........}

这里在注释一处获得了AMS,并且在注释二中调用到了AMS的attachApplication方法,注释三处创建出了ContextImpl方法,注释四处创建了Application对象;这里我们主要看注释二处的方法,这个方法最后会调用attachApplicationLocked方法:

private boolean attachApplicationLocked(@NonNull IApplicationThread thread,int pid, int callingUid, long startSeq) {.........thread.bindApplication(processName, appInfo,//1-------1app.sdkSandboxClientAppVolumeUuid, app.sdkSandboxClientAppPackage,providerList,instr2.mClass,profilerInfo, instr2.mArguments,instr2.mWatcher,instr2.mUiAutomationConnection, testMode,mBinderTransactionTrackingEnabled, enableTrackAllocation,isRestrictedBackupMode || !normalMode, app.isPersistent(),new Configuration(app.getWindowProcessController().getConfiguration()),app.getCompat(), getCommonServicesLocked(app.isolated),mCoreSettingsObserver.getCoreSettingsLocked(),buildSerial, autofillOptions, contentCaptureOptions,app.getDisabledCompatChanges(), serializedSystemFontMap,app.getStartElapsedTime(), app.getStartUptime());.........return true;}

这里只要看注释一处的方法,会调用到ActivityThread的bindApplication方法:

        public final void bindApplication(String processName, ApplicationInfo appInfo,String sdkSandboxClientAppVolumeUuid, String sdkSandboxClientAppPackage,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,SharedMemory serializedSystemFontMap,long startRequestedElapsedTime, long startRequestedUptime) {...........data.initProfilerInfo = profilerInfo;data.buildSerial = buildSerial;data.autofillOptions = autofillOptions;data.contentCaptureOptions = contentCaptureOptions;data.disabledCompatChanges = disabledCompatChanges;data.mSerializedSystemFontMap = serializedSystemFontMap;data.startRequestedElapsedTime = startRequestedElapsedTime;data.startRequestedUptime = startRequestedUptime;sendMessage(H.BIND_APPLICATION, data);//1------1}

这里给自身的Handler发送了H.BIND_APPLICATION标志,用Handler来处理,最后会触发handleBindApplication方法:

    private void handleBindApplication(AppBindData data) {.........final InstrumentationInfo ii;if (data.instrumentationName != null) {ii = prepareInstrumentation(data);} else {ii = null;}final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);//1---mConfigurationController.updateLocaleListFromAppContext(appContext);..........Application app;final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();try {// If the app is being launched for full backup or restore, bring it up in// a restricted environment with the base application class.app = data.info.makeApplicationInner(data.restrictedBackupMode, null);//2----// Propagate autofill compat stateapp.setAutofillOptions(data.autofillOptions);// Propagate Content Capture optionsapp.setContentCaptureOptions(data.contentCaptureOptions);sendMessage(H.SET_CONTENT_CAPTURE_OPTIONS_CALLBACK, data.appInfo.packageName);mInitialApplication = app;............if (!data.restrictedBackupMode) {if (!ArrayUtils.isEmpty(data.providers)) {installContentProviders(app, data.providers);//3-------3}}...........}}

在注释一处创建出了ContextImpl对象,继续再注释二处创建出了Application对象,最后在注释三处调用installContentProvider方法来安装内容提供器,所以我们看注释三处的方法:

private void installContentProviders(Context context, List<ProviderInfo> providers) {final ArrayList<ContentProviderHolder> results = new ArrayList<>();for (ProviderInfo cpi : providers) {........ContentProviderHolder cph = installProvider(context, null, cpi,//1-----1false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);if (cph != null) {cph.noReleaseNeeded = true;results.add(cph);}}try {ActivityManager.getService().publishContentProviders(getApplicationThread(), results);//2--------2} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}}

在注释一处遍历当前应用进程的ProviderInfo列表,得到每个Content Provider的ProviderInfo信息,并调用installProvider方法来启动Content Provider,在注释三处又会将这些Content Provider存储在AMS的mProviderMap中,以便后续调用。接下来看注释一处的方法:

private ContentProviderHolder installProvider(Context context,ContentProviderHolder holder, ProviderInfo info,boolean noisy, boolean noReleaseNeeded, boolean stable) {ContentProvider localProvider = null;IContentProvider provider;.............try {final java.lang.ClassLoader cl = c.getClassLoader();LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);if (packageInfo == null) {// System startup case.packageInfo = getSystemContext().mPackageInfo;}localProvider = packageInfo.getAppFactory().instantiateProvider(cl, info.name);//1-------1provider = localProvider.getIContentProvider();..........localProvider.attachInfo(c, info);//2-------2} catch (java.lang.Exception e) {if (!mInstrumentation.onException(null, e)) {throw new RuntimeException("Unable to get provider " + info.name+ ": " + e.toString(), e);}return null;}} else {provider = holder.provider;if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "+ info.name);}ContentProviderHolder retHolder;..............return retHolder;}

在注释一处,用ClassLoader实例化了一个localProvider,接着在注释二处进行参数的配置:

    private void attachInfo(Context context, ProviderInfo info, boolean testing) {..........ContentProvider.this.onCreate();}}

这个方法最后就会调用onCreate回调方法完成内容提供者的创建。

总结

总的来说还是ContextImpl到AMS的路数,不过这次ActivityThread的戏份比较少。
在这里插入图片描述


http://www.ppmy.cn/news/409233.html

相关文章

不花冤枉钱,推荐几个装机方案

不花冤枉钱&#xff0c;推荐几个装机方案 最近有几个朋友要装机&#xff0c;有上网玩的&#xff0c;有做j2ee的&#xff0c;我给出几个配置方案&#xff0c;本人比较喜欢3A平台&#xff0c;高性价比&#xff0c;升级容易&#xff0c;兼容性好。推荐这几款均不是游戏玩家的配置&…

USB接口供电不足以及相关故障

USB接口供电不足以及相关故障原文&#xff1a;晓天 天极网事由&#xff1a;   今天本来休息&#xff0c;可是公司打电话说是有一客户急需上门&#xff0c;原因是客户新买了一个移动硬盘&#xff0c;接在自己的笔记本后能够发现新硬件&#xff0c;可“我的电脑”里面却没有盘…

32位CPU的机器只能支持4GB的内存吗

现在内存条都是白菜价的时代&#xff0c;很多人手中都是4G大内存了。但是普通的32位操作系统只能认3G多的内存&#xff0c;有很多都是给白白的浪费掉了。近来很多人说使用补丁&#xff0c;能使32位的系统支持4G大容量内存。事实果真如此吗&#xff1f; 一&#xff0c;cpu的寻址…

vagrant简单学习使用

2019独角兽企业重金招聘Python工程师标准>>> 1.安装vagrant 旧版本的vagrant可以在http://downloads.vagrantup.com/下载&#xff0c;支持的系统平台有mac,debian/ubuntu, centos,windows。如果要下载最新版本的vagrant&#xff0c;需要FQ。大家各自找FQ工具。 2.下…

02-4设置第一启动项--U盘装系统中bios怎么设置USB启动

整个U盘启动里最关键的一步就是设置U盘启动了&#xff0c;本教程内只是以特定型号的电脑为例进行演示&#xff0c;鉴于各种电脑不同BIOS设置U盘启动各有差异&#xff0c;所以如果下面的演示不能适用于你的电脑&#xff0c;建议去百度或者谷歌搜索一下你的电脑或者与你的电脑类似…

解决:系统重装后,捷波悍马hao1的声卡驱动问题

系统重装后&#xff0c;出现安装了声卡驱动&#xff0c;但仍无法使用音量控制器&#xff0c;不出声音的问题&#xff1b; 现解决方法如下&#xff1a; 打开设备管理器&#xff0c;点击系统设备&#xff0c;如图所示的设备是否为&#xff1f;号&#xff08;图中系统已安装了驱动…

什么蓝牙耳机通话质量比较好?音质超好的四款蓝牙耳机测评

真无线蓝牙耳机和智能手机一样&#xff0c;开始成为出街必备的数码单品了。毕竟真无线蓝牙耳机外观时尚&#xff0c;又方便携带&#xff0c;也就自然而然的成为越来越多的人使用它的原因。那么真无线蓝牙耳机买什么好呢&#xff1f;毕竟现在的真无线蓝牙耳机品牌真的很多。下面…