当前位置:   article > 正文

Android 源码分析 (十一) ContentProvider 启动_getcontentprovider

getcontentprovider

ContentProvider (内容提供者) 属于四大组件之一,可以说它是在四大组件中开发者使用率最少的一个,它的作用就是进程间进行数据交互,底层采用 Binder 机制进行进程间通信。

下面我们就以分析 ContentProvider 工作流程为主来进行全面分析。

源码分析

query 到 AMS 调用过程

下面先来看一个代码示例,代码如下:

  1. fun getContactsLists(): MutableList<String> {
  2. var contactsLists: MutableList<String> = mutableListOf<String>()
  3. lateinit var cursor: Cursor
  4. try {
  5. //使用 getContentResolver() 查询联系人列表
  6. cursor = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null)
  7. //cursor 进行遍历
  8. if (cursor != null) {
  9. while (cursor.moveToNext()) {
  10. //电话号码
  11. val number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
  12. contactsLists.add("电话:$number")
  13. }
  14. }
  15. cursor.close()
  16. } catch (error: Exception) {
  17. logger.error("error:$error")
  18. }
  19. return contactsLists
  20. }
  21. //测试
  22. val contactsLists = getContactsLists()
  23. contactsLists.forEach { it -> println("通过ContentResolver获取联系人: $contactsLists") }

那么这一流程内部是怎么运行的,下面进入代码分析。老规矩,还是看一下该小节分析流程,这里以时序图为主

 通过上面代码示例想要 query 数据先要拿到 contentResolver 对象,通过父类 getContentResolver()方法获得,代码如下:

  1. @Override
  2. public ContentResolver getContentResolver() {
  3. return mBase.getContentResolver();
  4. }

这里的 mBase 是 Context 对象,它的实现类就是 ContextImpl 我们直接看它具体实现,代码如下:

  1. //ContextImpl.java
  2. @Override
  3. public ContentResolver getContentResolver() {
  4. return mContentResolver;
  5. }

这里的 getContentResolver 方法中返回了 ApplicationContentResolver 对象,它是 ContextImpl 的静态内部类,继承自 ContentResolver ,它在 ContextImpl 的构造方法中被创建,就会启动 ContentProvider,这里以上面我们示例 query 来进行分析,我们看它的具体实现,

  1. //ContentResolver.java
  2. public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
  3. @Nullable String[] projection, @Nullable Bundle queryArgs,
  4. @Nullable CancellationSignal cancellationSignal) {
  5. Preconditions.checkNotNull(uri, "uri");
  6. 1. 拿到 IContentProvider 对象,它是 ContentProvider 的本地代理
  7. IContentProvider unstableProvider = acquireUnstableProvider(uri);
  8. if (unstableProvider == null) {
  9. return null;
  10. }
  11. IContentProvider stableProvider = null;
  12. Cursor qCursor = null;
  13. try {
  14. try {
  15. 2. 调用 IContentProvider 的 query 函数来进行 query
  16. qCursor = unstableProvider.query(mPackageName, uri, projection,
  17. queryArgs, remoteCancellationSignal);
  18. } catch (DeadObjectException e) {
  19. //...
  20. return wrapper;
  21. } catch (RemoteException e) {
  22. return null;
  23. } finally {
  24. ...
  25. }
  26. }

通过 acquireUnstableProvider 方法拿到 ContentProvider 的本地代理对象,我们先来看下注释 1 的  acquireUnstableProvider 方法怎么拿到 ContentProvider 本地代理对象,代码如下:

  1. //ContentResolver.java
  2. public final IContentProvider acquireUnstableProvider(Uri uri) {
  3. if (!SCHEME_CONTENT.equals(uri.getScheme())) {
  4. return null;
  5. }
  6. String auth = uri.getAuthority();
  7. if (auth != null) {
  8. //调用内部抽象方法
  9. return acquireUnstableProvider(mContext, uri.getAuthority());
  10. }
  11. return null;
  12. }
  13. /** @hide */
  14. protected abstract IContentProvider acquireUnstableProvider(Context c, String name);
  1. //ContextImpl.java
  2. @Override
  3. protected IContentProvider acquireUnstableProvider(Context c, String auth) {
  4. return mMainThread.acquireProvider(c,
  5. ContentProvider.getAuthorityWithoutUserId(auth),
  6. resolveUserIdFromAuthority(auth), false);
  7. }

return 返回的对象,首先拿到 mMainThread 对象,然后调用它内部的 acquireProvider 方法

  1. //ActivityThread.java
  2. public final IContentProvider acquireProvider(
  3. Context c, String auth, int userId, boolean stable) {
  4. 1. acquireExistingProvider 方法主要检查 ActivityThread 全局变量 mProviderMap 中是否有目标 ContentProvider 存在,有就返回,没有就通过注释 2 处获取,
  5. final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
  6. if (provider != null) {
  7. return provider;
  8. }
  9. ContentProviderHolder holder = null;
  10. try {
  11. 2. 调用 IAcitivityManager 获取 ContentProviderHolder 对象
  12. holder = ActivityManager.getService().getContentProvider(
  13. getApplicationThread(), auth, userId, stable);
  14. } catch (RemoteException ex) {
  15. throw ex.rethrowFromSystemServer();
  16. }
  17. ...
  18. 3. 用来安装 ContentProvider
  19. holder = installProvider(c, holder, holder.info,
  20. true /*noisy*/, holder.noReleaseNeeded, stable);
  21. return holder.provider;
  22. }

总结

1 处的 acquireExistingProvider 方法内部会检查 ActivityThread 的全局变量 mProviderMap 中是否有 ContentProvider 存在

2 处 的 IActivityManager 的 getContentProvider 方法与 AMS 进行通信来获取

 3 是安装 ContentProvider,并将 ContentProvider 相关数据存储在 mProviderMap 中,起到缓存作用

我们现在来看下AMS 的 getContentProvider 方法具体实现,代码如下:

  1. //AMS.java
  2. @Override
  3. public final ContentProviderHolder getContentProvider(
  4. IApplicationThread caller, String name, int userId, boolean stable) {
  5. enforceNotIsolatedCaller("getContentProvider");
  6. ...
  7. //调动内部 getContentProviderImpl 方法
  8. return getContentProviderImpl(caller, name, null, stable, userId);
  9. }
  1. //AMS.java
  2. private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
  3. String name, IBinder token, boolean stable, int userId) {
  4. ContentProviderRecord cpr;
  5. ContentProviderConnection conn = null;
  6. ProviderInfo cpi = null;
  7. ..1. 获取目标 ContentProvider 应用程序进程的信息,如果进程已经启动就调用注释 2 ,否则调用注释 3
  8. ProcessRecord proc = getProcessRecordLocked(
  9. cpi.processName, cpr.appInfo.uid, false);
  10. if (proc != null && proc.thread != null && !proc.killed) {
  11. if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER,
  12. "Installing in existing process " + proc);
  13. if (!proc.pubProviders.containsKey(cpi.name)) {
  14. checkTime(startTime, "getContentProviderImpl: scheduling install");
  15. proc.pubProviders.put(cpi.name, cpr);
  16. try {
  17. 2. 调用 IApplicationThreadscheduleInstallProvider 函数
  18. proc.thread.scheduleInstallProvider(cpi);
  19. } catch (RemoteException e) {
  20. }
  21. }
  22. } else {
  23. checkTime(startTime, "getContentProviderImpl: before start process");
  24. 3. 启动新进程
  25. proc = startProcessLocked(cpi.processName,
  26. cpr.appInfo, false, 0, "content provider",
  27. new ComponentName(cpi.applicationInfo.packageName,
  28. cpi.name), false, false, false);
  29. checkTime(startTime, "getContentProviderImpl: after start process");
  30. if (proc == null) {
  31. Slog.w(TAG, "Unable to launch app "
  32. + cpi.applicationInfo.packageName + "/"
  33. + cpi.applicationInfo.uid + " for provider "
  34. + name + ": process is bad");
  35. return null;
  36. }
  37. }
  38. cpr.launchingApp = proc;
  39. mLaunchingProviders.add(cpr);
  40. } finally {
  41. Binder.restoreCallingIdentity(origId);
  42. }
  43. }
  44. ....
  45. }

2 位置执行流程,首先调用 ActivityThread 的内部类 IApplication 的scheduleInstallProvider 函数,然后通过 H sendMessage 通知进行安装 ContentProvider

看3位置处我们直接看 ActivityThread main 函数,代码如下

  1. //AMS.java
  2. final ProcessRecord startProcessLocked(String processName,
  3. ApplicationInfo info, boolean knownToBeDead, int intentFlags,
  4. String hostingType, ComponentName hostingName, boolean allowWhileBooting,
  5. boolean isolated, boolean keepIfLarge) {
  6. return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
  7. hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
  8. null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
  9. null /* crashHandler */);
  10. }

我们直接看 ActivityThread main 函数,代码如下

  1. //ActivityThread.java
  2. //通过反射调用执行的
  3. public static void main(String[] args) {
  4. ....
  5. //主线程消息循环
  6. Looper.prepareMainLooper();
  7. //创建 ActivityThread 对象
  8. ActivityThread thread = new ActivityThread();
  9. //Application,Activity 入口
  10. thread.attach(false);
  11. if (sMainThreadHandler == null) {
  12. sMainThreadHandler = thread.getHandler();
  13. }
  14. if (false) {
  15. Looper.myLooper().setMessageLogging(new
  16. LogPrinter(Log.DEBUG, "ActivityThread"));
  17. }
  18. // End of event ActivityThreadMain.
  19. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
  20. Looper.loop();
  21. throw new RuntimeException("Main thread loop unexpectedly exited");
  22. }

主要看 ActivityThread attach 函数具体实现,代码如下:

  1. //ActivityThread.java
  2. private void attach(boolean system) {
  3. ...
  4. final IActivityManager mgr = ActivityManager.getService();
  5. try {
  6. 关联 Application
  7. mgr.attachApplication(mAppThread);
  8. } catch (RemoteException ex) {
  9. throw ex.rethrowFromSystemServer();
  10. }
  11. ...
  12. }

从上得到 AMS 的代理类 IActivityManager ,在注释 调用 AMS 的 attachApplication 函数,并将 IApplicationThread 对象传入 AMS 保持应用进程和 AMS 跨进程通信,应用程序调用 AMS 的过程就分析完了,下面我们分析 AMS 到应用程序进程的 ContentProvider 安装过程

AMS 启动 ContentProvider 的过程

先看流程图

 

  1. //AMS.java
  2. private final boolean attachApplicationLocked(IApplicationThread thread,
  3. int pid) {
  4. ...
  5. thread.bindApplication(processName, appInfo, providers,
  6. app.instr.mClass,
  7. profilerInfo, app.instr.mArguments,
  8. app.instr.mWatcher,
  9. app.instr.mUiAutomationConnection, testMode,
  10. mBinderTransactionTrackingEnabled, enableTrackAllocation,
  11. isRestrictedBackupMode || !normalMode, app.persistent,
  12. new Configuration(getGlobalConfiguration()), app.compat,
  13. getCommonServicesLocked(app.isolated),
  14. mCoreSettingsObserver.getCoreSettingsLocked(),
  15. buildSerial);
  16. ...
  17. }

在 attachApplicationLocked 函数中调用了 thread.bindApplication 方法,thread 是 IApplicationThread ,这里和 IActivityManager 一样采用了 aidl 进行进程间传输数据,我们回到 ActivityThread 内部类 ApplicationThread 的 bindApplication 方法

  1. //ActivityThread.java
  2. public final void bindApplication(String processName, ApplicationInfo appInfo,
  3. List<ProviderInfo> providers, ComponentName instrumentationName,
  4. ProfilerInfo profilerInfo, Bundle instrumentationArgs,
  5. IInstrumentationWatcher instrumentationWatcher,
  6. IUiAutomationConnection instrumentationUiConnection, int debugMode,
  7. boolean enableBinderTracking, boolean trackAllocation,
  8. boolean isRestrictedBackupMode, boolean persistent, Configuration config,
  9. CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
  10. String buildSerial) {
  11. if (services != null) {
  12. // Setup the service cache in the ServiceManager
  13. ServiceManager.initServiceCache(services);
  14. }
  15. setCoreSettings(coreSettings);
  16. AppBindData data = new AppBindData();
  17. data.processName = processName;
  18. data.appInfo = appInfo;
  19. data.providers = providers;
  20. data.instrumentationName = instrumentationName;
  21. data.instrumentationArgs = instrumentationArgs;
  22. data.instrumentationWatcher = instrumentationWatcher;
  23. data.instrumentationUiAutomationConnection = instrumentationUiConnection;
  24. data.debugMode = debugMode;
  25. data.enableBinderTracking = enableBinderTracking;
  26. data.trackAllocation = trackAllocation;
  27. data.restrictedBackupMode = isRestrictedBackupMode;
  28. data.persistent = persistent;
  29. data.config = config;
  30. data.compatInfo = compatInfo;
  31. data.initProfilerInfo = profilerInfo;
  32. data.buildSerial = buildSerial;
  33. //发送消息给 H 类
  34. sendMessage(H.BIND_APPLICATION, data);
  35. }

ActivityThread 内部类 H 收到消息,开始处理 BIND_APPLICSTION消息,代码如下:

  1. //ActivityThread.java
  2. ...
  3. public void handleMessage(Message msg) {
  4. ...
  5. case BIND_APPLICATION:
  6. AppBindData data = (AppBindData)msg.obj;
  7. handleBindApplication(data);
  8. break;
  9. ...
  10. }
  11. ...
  1. //ActivityThread.java
  2. ...
  3. public void handleMessage(Message msg) {
  4. ...
  5. case BIND_APPLICATION:
  6. AppBindData data = (AppBindData)msg.obj;
  7. handleBindApplication(data);
  8. break;
  9. ...
  10. }
  11. ...
  1. //ActivityThread.java
  2. private void handleBindApplication(AppBindData data) {
  3. ...
  4. 1. 创建 ContentImpl
  5. final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
  6. ...
  7. 2 通过反射创建 Instrumentation
  8. final ClassLoader cl = instrContext.getClassLoader();
  9. mInstrumentation = (Instrumentation)
  10. cl.loadClass(data.instrumentationName.getClassName()).newInstance();
  11. } catch (Exception e) {
  12. ...
  13. }
  14. final ComponentName component = new ComponentName(ii.packageName, ii.name);
  15. 3 处初始化 Instrumentaion
  16. mInstrumentation.init(this, instrContext, appContext, component,
  17. data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
  18. ....
  19. try {
  20. 创建 Application
  21. Application app = data.info.makeApplication(data.restrictedBackupMode, null);
  22. mInitialApplication = app;
  23. if (!data.restrictedBackupMode) {
  24. if (!ArrayUtils.isEmpty(data.providers)) {
  25. 5 ContentProvider 启动的
  26. installContentProviders(app, data.providers);
  27. mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
  28. }
  29. }
  30. try {
  31. mInstrumentation.onCreate(data.instrumentationArgs);
  32. }
  33. catch (Exception e) {
  34. ...
  35. }
  36. try {
  37. 6 调用 Application onCreate 生命周期方法
  38. mInstrumentation.callApplicationOnCreate(app);
  39. } catch (Exception e) {
  40. ...
  41. }
  42. }

注释 5 看 ContentProvider 是如何启动的,代码如下:

  1. //ActivityThread.java
  2. private void installContentProviders(
  3. Context context, List<ProviderInfo> providers) {
  4. final ArrayList<ContentProviderHolder> results = new ArrayList<>();
  5. 1 遍历当前应用程序进程的 ProviderInfo 列表,得到每个 ContentProvicer 的存储信息
  6. for (ProviderInfo cpi : providers) {
  7. if (DEBUG_PROVIDER) {
  8. StringBuilder buf = new StringBuilder(128);
  9. buf.append("Pub ");
  10. buf.append(cpi.authority);
  11. buf.append(": ");
  12. buf.append(cpi.name);
  13. Log.i(TAG, buf.toString());
  14. }
  15. 2 调用 installProvider 方法来启动这些 ContentProvider
  16. ContentProviderHolder cph = installProvider(context, null, cpi,
  17. false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
  18. if (cph != null) {
  19. cph.noReleaseNeeded = true;
  20. results.add(cph);
  21. }
  22. }
  23. try {
  24. 3 处是将启动了的 ContentProvider 存入 AMS 的 mProviderMap 中 就是用来缓存启动过的 ContentProvidec
  25. ActivityManager.getService().publishContentProviders(
  26. getApplicationThread(), results);
  27. } catch (RemoteException ex) {
  28. throw ex.rethrowFromSystemServer();
  29. }
  30. }

现在来看调用 installProvider 方法来启动这些 ContentProvider

  1. //ActivityThread.java
  2. private ContentProviderHolder installProvider(Context context,
  3. ContentProviderHolder holder, ProviderInfo info,
  4. boolean noisy, boolean noReleaseNeeded, boolean stable) {
  5. ....
  6. try {
  7. final java.lang.ClassLoader cl = c.getClassLoader();
  8. 1 通过反射实例化 ContentProvider 对象
  9. localProvider = (ContentProvider)cl.
  10. loadClass(info.name).newInstance();
  11. provider = localProvider.getIContentProvider();
  12. ...
  13. 2处调用它的 attachInfo 方法
  14. localProvider.attachInfo(c, info);
  15. } catch (java.lang.Exception e) {
  16. if (!mInstrumentation.onException(null, e)) {
  17. throw new RuntimeException(
  18. "Unable to get provider " + info.name
  19. + ": " + e.toString(), e);
  20. }
  21. return null;
  22. }
  23. ....
  24. }

在注释 2 处调用它的 attachInfo 方法,代码如下

  1. //ContentProvider.java
  2. public void attachInfo(Context context, ProviderInfo info) {
  3. attachInfo(context, info, false);
  4. }
  5. private void attachInfo(Context context, ProviderInfo info, boolean testing) {
  6. mNoPerms = testing;
  7. if (mContext == null) {
  8. mContext = context;
  9. if (context != null) {
  10. mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
  11. Context.APP_OPS_SERVICE);
  12. }
  13. mMyUid = Process.myUid();
  14. if (info != null) {
  15. setReadPermission(info.readPermission);
  16. setWritePermission(info.writePermission);
  17. setPathPermissions(info.pathPermissions);
  18. mExported = info.exported;
  19. mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
  20. setAuthorities(info.authority);
  21. }
  22. /**
  23. * 安装成功,调用生命周期函数 onCreate
  24. */
  25. ContentProvider.this.onCreate();
  26. }
  27. }
  28. public abstract boolean onCreate();

可以看到最后在 ContentProvider 的 attachInfo 函数中进行调用了抽象方法 onCreate, 那么它的子类就会进行实现 onCreate 达到启动成功的通知。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/892109
推荐阅读
相关标签
  

闽ICP备14008679号