zoukankan      html  css  js  c++  java
  • 浅析调用android的content provider(一)

            在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.   
    6.     Looper.loop();  
    7.   
    8.     // ......  
    9. }  

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


  • 相关阅读:
    Builder与Factory,殊途同归!
    IIS中的身份验证
    如何给项目选择合适语言(转)
    动态行转列
    ORACLE系统表大全(转)
    C# 操作Word文档(转)
    产品化思维之分层的思想
    开发管理目前开发工作的问题分析和诊断
    MongoDB数据插入、删除、更新、批量更新某个字段
    学习正则表达式
  • 原文地址:https://www.cnblogs.com/zsw-1993/p/4879932.html
Copyright © 2011-2022 走看看