在Android下,查询联系人、通话记录等,需要用到content provider。但是,调用content provider时,Android框架内部是如何做的呢?这一系列文章就是解决这个问题的,所采用的开发环境及源码都是基于Android 1.6版本。
  • 概述

总的来说此问题分为两个步骤:

  1. 初始化content provider。这一阶段主要是参照AndroidManifest.xml,初始化content provider。注意这里只有当包含content provider的进程运行的时候,才会对该进程内所有的content provider进行初始化。其它provider是按需初始化的(后续文章会介绍该问题)。
  2. 调用content provider,进行数据库操作。这个调用通常发生在用户定义的Activity子类的相关接口内。调用时,首先会获取对应的content provider对象(有可能是代理对象)。然后,再调用(直接调用或者通过IBinder接口)。

本文主要探讨第一个问题:初始化content provider。

  • 应用进程的管理模型

Android框架内,应用程序Java代码的入口为ActivityThread.main。用来管理不同的应用的服务名称为activity。它的模型大致为:

左边为多个应用进程,每个进程中有个主线程ActivityThread.main,Looper.loop()是主线程的消息循环。所有的应用进程都是通过IBinder机制和ActivityManagerService进程进行交互的。应用进程为了能调用activity服务进程的ActivityManagerService中的方法,必须通过ActivityManagerProxy类。activity服务进程则通过ApplicationThreadProxy类与服务进程的ApplicationThread通信。ApplicationThread的作用主要是将activity服务进程的调用转换为ActivityThread主线程中的消息,从而保证ActivityManagerService的调用是异步的。

右边的activity服务进程是所有应用的服务进程,用于管理应用进程。启动新的应用进程时,会向zygote服务进程发送socket消息。zygote接收到消息后,则会启动新的delvik虚拟机,然后运行ActivityThread.main,启动新的应用。

  • PackageManagerService

PackageManagerService也是一个服务,是用来管理手机内所有的apk包的。调用它的方式和调用ActivityManagerService是一样的,通过IBinder。它的初始化入口为PackageManagerService.main:

[java] view plaincopy
  1. public static final IPackageManager main(Context context, boolean factoryTest) {
  2. PackageManagerService m = new PackageManagerService(context, factoryTest);
  3. ServiceManager.addService("package", m);
  4. return m;
  5. }

main方法主要是创建一个PackageManagerService,然后注册到ServiceManager中,名称为"package”。PackageManagerService的构造函数中,会去查找系统目录和应用目录下的apk文件,以获取应用的包相关的信息;比如:包名称,包含的Acvity、Provider等。

[java] view plaincopy
  1. // ……
  2. scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM, scanMode);
  3. // ……
  4. scanDirLI(mAppInstallDir, 0, scanMode);
  5. // ……

scanDirLI函数中,对于每个package,使用函数scanPackageLI解析其中的信息(此处应该是读取AndroidManifest.xml)。scanPackageLI检查相关信息后,又会调用另一个scanPackageLI。这个函数内部会扫描到手机内所有的Provider信息:

[java] view plaincopy
  1. PackageParser.Provider p = pkg.providers.get(i);
  2. p.info.processName = fixProcessName(pkg.applicationInfo.processName,
  3. p.info.processName, pkg.applicationInfo.uid);
  4. mProvidersByComponent.put(new ComponentName(p.info.packageName,
  5. p.info.name), p);

mProvidersByComponent保存了所有的provider信息,这部分数据源自于manifest。每个数据包含了PackageParser.Provider、包名称和Provider的类名。

到这里,我们可以看到,PackageManagerService真的是用来管理手机的应用包的。通过它可以知道所有的系统可用资源。当然这些资源只是一些静态信息。通过这些信息,可以创建应用进程、初始化相关的Android组件。

  • 应用进程的初始化

先看一下ActivityThread.main的实现,它创建了一个ActivityThread对象,初始化之后,进入消息循环。

[java] view plaincopy
  1. public static final void main(String[] args) {
  2. // ......
  3. ActivityThread thread = new ActivityThread();
  4. thread.attach(false);
  5. Looper.loop();
  6. // ......
  7. }

attach函数中,回去调用ActivityManagerService.attachApplication方法。

[java] view plaincopy
  1. mgr.attachApplication(mAppThread);

此时,会进入ActivityManagerService的进程空间,进入方法attachApplicationLocked,它会去获取和当前客户端应用程序关联的Provider信息。

[java] view plaincopy
  1. List providers = generateApplicationProvidersLocked(app);

根据上面的信息,很容易知道此处是通过PackageManagerService获取Provider信息的。参数app表明,只是取运行在该app内的Provider。根据Android的文档,content provider必须在对应的AndroidManifest.xml中定义。默认情况下,是运行在安装包名称命名的进程里面。你也可以在android:process属性中制定所属的进程名称。另一个重要的属性android:multiprocess则可以指定provider的初始化方式,是分散在调用端进程中,从而避免进程间通信;还是只初始化在某个进程内,各个调用端只保留provider的代理。

随后,通过下面的方法调用,返回到应用程序的进程空间,参数中包含了上面获得的providers。此处的thread实际上就是应用端的ApplicationThread对象。

[java] view plaincopy
  1. thread.bindApplication(processName, app.instrumentationInfo != null
  2. ? app.instrumentationInfo : app.info, providers,
  3. app.instrumentationClass, app.instrumentationProfileFile,
  4. app.instrumentationArguments, app.instrumentationWatcher, testMode,
  5. isRestrictedBackupMode, mConfiguration, getCommonServicesLocked());

ApplicationThread.bindApplication会发送BIND_APPLICATION消息给主线程。主线程会调用ActivityThread.handleBindApplication方法。这个函数里面主要分两步:一是根据需要创建Application、ApplicationContext和ApplicationContentResolver对象。因为,有可能多个apk运行在一个进程中,那么它们内部的组件(Component)执行的上下文(context)是不一样的。这几个对象实际上就是provider执行的上下文。二是根据传递过来的provider信息,创建provider实例,并保存在ActivityThread.mProviderMap中。具体的实例化provider过程如下(下面的代码位于ActivityThread.handleBindApplication中):

[java] view plaincopy
  1. List<ProviderInfo> providers = data.providers;
  2. if (providers != null) {
  3. installContentProviders(app, providers);
  4. }

获取ActivityManagerService传递过来的provider信息,并在本进程中初始化。具体的,installContentProviders方法中,会对每个provider调用installProvider方法:

[java] view plaincopy
  1. IContentProvider cp = installProvider(context, null, cpi, false);

installProvider方法中,主要是实例化Provider,并保存到mProviderMap中:

[java] view plaincopy
  1. final java.lang.ClassLoader cl = c.getClassLoader();
  2. localProvider = (ContentProvider)cl.
  3. loadClass(info.name).newInstance();
  4. provider = localProvider.getIContentProvider();
  5. // ......
  6. // Cache the pointer for the remote provider.
  7. String names[] = PATTERN_SEMICOLON.split(info.authority);
  8. for (int i=0; i<names.length; i++) {
  9. ProviderRecord pr = new ProviderRecord(names[i], provider,
  10. localProvider);
  11. try {
  12. provider.asBinder().linkToDeath(pr, 0);
  13. mProviderMap.put(names[i], pr);
  14. } catch (RemoteException e) {
  15. return null;
  16. }
  17. }

上面的installContentProviders方法执行完成之后,会调用ActivityManagerService.publishContentProviders方法,将provider注册到ActivityManagerService中,方便其它应用进程获取。这里面有两个参数,一个是ApplicationThread对象,另一个是provider实例信息。

[java] view plaincopy
  1. try {
  2. ActivityManagerNative.getDefault().publishContentProviders(
  3. getApplicationThread(), results);
  4. } catch (RemoteException ex) {
  5. }

ActivityManagerService.publishContentProviders的实现也很简单,主要是将provider信息保存到ActivityManagerService.mProvidersByName中。具体参见源码。

  • 总结

本文主要介绍了Android系统内provider的初始化,Android系统默认是会初始化一些provider的,比如:ContactsProvider。它们的初始化和本文介绍的流程应该差不多,主要是在应用进程初始化时获取provider的信息,然后实例化provider,最后将实例化的provider保存到ActivityManagerService中,供其它应用进程使用。需要说明的一点是,应用进程内可以运行多个apk中的组件。

下一篇文章会介绍调用provider的流程。

分享到: 

转载于:https://www.cnblogs.com/zsw-1993/p/4879932.html

浅析调用android的content provider(一)相关推荐

  1. Android 开发 Content Provider 使用 demo

    在我们Android开发中难免会用到Content Provider,主要是为了实现进程间访问数据,数据库是Android开发中最基本的数据保存方式,但由于数据库的私有性,我们无法对外提供或获取信息, ...

  2. Android Content Provider Security

    0x00 科普 内容提供器用来存放和获取数据并使这些数据可以被所有的应用程序访问.它们是应用程序之间共享数据的唯一方法:不包括所有Android软件包都能访问的公共储存区域.Android为常见数据类 ...

  3. Android应用程序组件Content Provider在应用程序之间共享数据的原理分析(1)

             在Android系统中,不同的应用程序是不能直接读写对方的数据文件的,如果它们想共享数据的话,只能通过Content Provider组件来实现.那么,Content Provide ...

  4. Content Provider之一大菊观

    题记:这篇是对content provider的一个全局的认识篇,包括一些基本概念和用法,故而借用下大酒神的大菊观附体~ 主要包括以下几个方面: provider执行原理以及相关的概念: 通过prov ...

  5. Content Provider启动浅析

    一.自己的理解 对于content provide的启动我是这样认为的,要用ContentResolver去获得一个contentProvider,在这的获得的过程中, 1.如果本应用之前有conte ...

  6. Android应用程序组件Content Provider的启动过程源代码分析(6)

        Step 17. ActivityThread.installProvider         这个函数定义在frameworks/base/core/java/android/app/Act ...

  7. Android应用程序组件Content Provider的共享数据更新通知机制分析(3)

            3. 数据更新通知的发送过程        在前面这篇文章Android应用程序组件Content Provider应用实例介绍的应用程序Acticle中,当调用ArticlesAda ...

  8. Android应用程序组件Content Provider的启动过程源代码分析(1)

             通过前面的学习,我们知道在Android系统中,Content Provider可以为不同的应用程序访问相同的数据提供统一的入口.Content Provider一般是运行在独立的进 ...

  9. android 组件(activity,service,content provider,broadcast receiver,intent)详解

    Android应用程序由若干个不同类型的组件组合而成,每一个组件具有其特定的安全保护设计方式,它们的安全直接影响到应用程序的安全.Android应用程序组件的主要类型有:活动(Activity),服务 ...

最新文章

  1. php跨域共享session
  2. OpenAI NLP最新进展:通过无监督学习提升语言理解
  3. UC 伯克利华人一作:卷积让视觉 Transformer 性能更强,ImageNet 继续刷点!
  4. application配置token_Kerrigan:配置中心管理UI的实现思路和技术细节
  5. Shell终端快捷键总结(mac)
  6. java md5 密钥_java加密算法--MD5加密和哈希散列带秘钥加密算法源码
  7. Centos环境下部署游戏服务器-iptables
  8. linux url解码,js对url进行编码和解码(三种方式区别)
  9. centos 关闭命令行警报声
  10. 504 Gateway Time-out 错误处理记录
  11. python描述符 descriptor
  12. PS快捷键大全:掌握这些快捷键,「附软件」让你秒变PS大师
  13. flutter 一键生成安卓和ios应用图标
  14. 时间复杂度与空间复杂度
  15. C printf() 详解之终极无惑
  16. 美国大力发展量子产业,国会直接指定能源部制定量子系统访问路线图
  17. 基于CNN和LSTM的气象图降水预测示例
  18. 租车App第一次迭代报告
  19. python知识图谱代码_贪心学院第二个主题代码--Python岗位知识图谱
  20. 汇编语言 实现1.将数据区buf1中的10个数,传送到数据区buf2 2.计算buf1数据的累加和

热门文章

  1. wait( )和 waitpid( )
  2. RxPermissions 源码解析之举一反三
  3. mysql用户的权限分配
  4. web流程设计器 工作流的 整合视频教程 activiti画图 SSM和独立部署
  5. [ASP.NET AJAX]类似.NET框架的JavaScript扩展
  6. 40 个重要的 HTML5 面试问题及答案
  7. linux 开启防火墙的指定端口
  8. 强大的独立日期选择器(date picker)插件 - Kalendae
  9. Android 更改签名
  10. 黄聪:【强烈推荐】搜索引擎排名决定一切吗!