在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. textarea标签中多出的空格

    //这么写才能被正确渲染 <textarea></textarea> //这样就会有空格 <textarea> </textarea> 不能换行,涨姿势

  2. Thinkphp 数据库配置参数

    mysql配置 'DB_USER' => array( 'DB_TYPE' => 'mysql', 'DB_HOST' => '127.0.0.1', 'DB_NAME' => ...

  3. HDU2056

    /*  * 指数型母函数  */ #include<cstdio> #define mod 100 typedef long long LL;//杭电需用int64 int powerMo ...

  4. 用CSS添加选中文字的背景色

  5. 基础篇-1.5Java的数组

    1 引言 每一种编程语言都有其自身的数组概念,大同小异,都是为了存储一堆数据,而Java的数组是用来存储相同类型的数据,如声明一个arr[10]数组,可以用来代替声明10个变量. 2 声明和创建数组 ...

  6. CentOS7系统ifconfig无法使用的解决方法

    在使用RAKsmart美国服务器的时候,如果安装的CentOS7操作系统的话,可能会经常用到“ifconfig”命令.ifconfig命令大多是用于CentOS 6版本下面,主要用于查看网卡配置信息. ...

  7. Elasticsearch连接类(带密码)

    /** * 获取ES连接类 * * @author 陈康 * @description * @create 2019/08/15 **/ @Component("ElasticsearchR ...

  8. 机房收费系统vb.net之打包与部署(二)

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/wangdan199112/article/details/28286365             ...

  9. day37 01-上次课内容回顾

  10. 【CodeVS】1023 GPA计算

    1023 GPA计算 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 青铜 Bronze 题目描述 Description 小松终于步入了大学的殿堂,带着兴奋和憧憬,他参加了信息科学 ...