在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
plain
copy

  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
plain
copy

  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
plain
copy

  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
plain
copy

  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
plain
copy

  1. mgr.attachApplication(mAppThread);

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

[java] view
plain
copy

  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
plain
copy

  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
plain
copy

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

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

[java] view
plain
copy

  1. IContentProvider cp = installProvider(context, null, cpi, false);

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

[java] view
plain
copy

  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
plain
copy

  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的流程。

分享到: 

浅析调用android的content provider(一)的更多相关文章

  1. android学习十二(android的Content Provider(内容提供器)的使用)

    文件存储和SharePreference存储以及数据存储一般为了安全,最好用于当前应用程序中訪问和存储数据.内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能 ...

  2. android笔记 : Content provider内容提供器

    内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能. 内容提供器的用法一般有两种,一种是使用现有的内容提供器来读取和操作相应程序中的数据,另一种是创建自己的内 ...

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

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6967204 在Android系统中,不同的应用 ...

  4. Android Content Provider Security(转)

    四大组件之一-content provider安全详解 原帖地址:http://drops.wooyun.org/tips/4314 0x00 科普 内容提供器用来存放和获取数据并使这些数据可以被所有 ...

  5. Android Contacts (android通讯录读取)-content provider

    Content Provider 在数据处理中,Android通常使用Content Provider的方式.Content Provider使用Uri实例作为句柄的数据封装的,很方便地访问地进行数据 ...

  6. Android Content Provider Guides

    Android Content Provider Guides Content Providers管理对结构化数据集的访问.它们包装数据,并且提供一种定义数据安全的机制. Content provid ...

  7. Android Content Provider基础

    Android Content Provider基础 Content Providers Content providers管理对一个结构化的数据集合的访问.它们封装了数据,并且提供了保护数据安全性的 ...

  8. Android 内容提供器(Content Provider)介绍

    内容提供器(Content Provider)主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访问数据的安全性.目前,使用内容 ...

  9. Android学习五:Content Provider 使用

    1ContentProvider相关知识1.1在安卓应用中,通过文件方式对外共享数据,需要进行文件操作读写数据:采用sharedpreferences共享数据,需要使用sharedpreference ...

随机推荐

  1. Html-前端表单校验

    先前端校验再跳转action <form action="/hr/kefu/edit_dangan_do.html" method="post" enct ...

  2. H5C3--background中cover,背景样式,提升响应区域+精灵图的使用

    一.cover的使用 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...

  3. 关于github 代码管理,协作开发

    公司要用github 进行项目管理,  了解了一下github相关权限管理. 并做笔记如下: 个人账户可以建立公有/私有  repository ,  公有的全天下的人都能看到,私有的全天下人都看不到 ...

  4. jnhs中国省市县区mysql数据表不带gps坐标

    1.查省 SELECT * FROM china WHERE china.Pid=0 2.查市 SELECT * FROM chinaWHERE china.Pid=330000 3.查区 SELEC ...

  5. CSS利用filter/opacity实现背景透明

    先看看众所周知的解决方案 如果我们想要一个半透明背景,有两种实现方式: 1.利用CSS和opacity属性 .opacity { filter:alpha(opacity=);/*IE浏览器*/ op ...

  6. python基础--线程、进程

    并发编程: 操作系统:(基于单核研究) 多道技术: 1.空间上的复用 多个程序共用一个计算机 2.时间上的复用 切换+保存状态 例如:洗衣 烧水 做饭 切换: 1.程序遇到IO操作系统会立刻剥夺着CP ...

  7. ue4 fstring 和std::string互转

    https://forums.unrealengine.com/development-discussion/c-gameplay-programming/6517-convert-std-strin ...

  8. 配置管理 ACM 在高可用服务 AHAS 流控降级组件中的应用场景

    应用配置管理(Application Configuration Management,简称 ACM)是一款应用配置中心产品.基于ACM您可以在微服务.DevOps.大数据等场景下极大地减轻配置管理的 ...

  9. c++新特性实验(1)预处理

    1.参考资料 1.1 C++ C++17 标准文档(正式)  :    https://www.iso.org/standard/68564.html C++ 标准文档(草案)      :   ht ...

  10. vue制作幻灯片-左右移动

    组件中: <template> <div class="slide-show" @mouseover="clearInv" @mouseout ...