美文网首页Android 基础知识Android开发Android开发经验谈
一图解惑之Android管理ContentProvider结构

一图解惑之Android管理ContentProvider结构

作者: Catsuo | 来源:发表于2018-01-10 12:51 被阅读52次

    准备

    在上图之前还是先简单总结下相关的数据结构类。

    在Framework中可以理解为一个ContentProviderRecord对应应用层中的一个ContentProvider,主要的数据结构类和其相关的成员变量在下面简要描述:

    • ContentProviderRecord : 一个表示应用层定义的ContentProvider的数据结构。相关成员变量:
    1. info :ProviderInfo 描述该ContentProvider的具体信息。
    2. appInfo :ApplicationInfo 描述定义该ContentProvider的应用的信息。
    3. name :ComponentName 描述标识该ContentProvider的ComponentName。
    4. singleton :boolean 表示该ContentProvider是否是跨用户单例的,所谓跨用户单例即系统中只会保存一个这样的ContentProviderRecord结构,多个用户使用的是同一个ContentProviderRecord。当第三方应用的ContentProvider想要定义为跨用户单例时,需要添加INTERACT_ACROSS_USERS权限。
    5. connections :ArrayList<ContentProviderConnection> 保存访问该ContentProvider的客户端的连接,用于在比如当前ContentProvider所在进程挂掉等时刻通知客户端进程。因为一个ContentProvider定义后可以被系统中不同的进程访问,所以此处使用一个容器来存储。

    从该结构得到信息:
    ContentProviderRecord中主要保存当前ContentProvider信息的是info成员变量,除此之外该结构中还保存了定义当前ContentProvider的进程的相关信息。另外由于一个ContentProvider可以被多个进程访问,所以该结构中用一个容器来保存客户端与访问的ContentProvider之间的连接关系。

    • ProviderInfo :真正描述应用层定义的ContentProvider的属性信息。相关成员变量:
    1. authority :String 定义ContentProvider时指定的authority。
    2. readPermission :String 定义ContentProvider时指定的readPermission。
    3. writePermission :String 定义ContentProvider时指定的writePermission。
    4. grantUriPermissions :boolean 定义ContentProvider时指定的grantUriPermissions。

    从该结构得到信息:
    该结构中基本就是保存定义ContentProvider时声明的各个属性。PMS去扫描AndroidManifest文件时得到的也是该结构。

    • ContentProviderConnection :表示一个访问ContentProvider的客户端连接。相关成员变量:
    1. provider :ContentProviderRecord 描述其访问的是哪个ContentProvider。
    2. client :ProcessRecord 描述该客户端进程。
    3. stableCount :int 用来记录稳定连接的数量。
    4. unstableCount :int 用来记录非稳定连接的数量。
      这两个连接数来帮助系统判断当前的ContentProvider还有没有客户端在访问。
      这里需要多说几句,站在系统的角度看稳定连接与非稳定连接的区别主要是在ContentProvider发生一些事件时,采用两种不同的连接访问该ContentProvider的客户端会被不同的策略处理。比如当ContentProvider所在的进程挂掉时,系统会遍历该ContentProviderRecord的connections,处理正在访问该ContentProvider的客户端,如果此时客户端采用稳定连接访问,那么系统会连同该客户端一起kill。如果此时客户端采用非稳定连接访问,则系统只是通知客户端访问的ContentProvider已经死亡。
      那么具体什么时候是稳定连接,什么时候是非稳定连接。这取决于访问数据的方式,一般来说,查询数据会使用非稳定的连接,而增删改则使用的是稳定的连接。

    从该结构得到信息:
    该结构主要是用于描述一个访问ContentProvider的客户端连接,并保存了客户端在访问ContentProvider时的一些属性。系统中运行的一个ContentProvider是可能被多个客户端访问的,系统就用该结构来记录一个客户端到它访问的ContentProvider之间的连接。

    上图

    额哼哼...小二,上图:


    Provider结构图 (1).png

    查看原图

    简要总结:

    1. 图中根节点ProviderMap是服务于AMS的一个类,主要负责管理ContentProviderRecord。

    2. ProviderMap中有四个集合:
      mSingletonByName:
      直接以定义ContentProvider时的authority为key来保存所有正在运行的跨用户单例的ContentProvider所对应的ContentProviderRecord结构。
      mSingletonByClass:
      直接以ContentProvider对应的ComponentName为key来保存所有正在运行的跨用户单例的ContentProvider所对应的ContentProviderRecord结构。
      mProvidersByNamePerUser:
      先用userId做一级分类,再在每一个userId中以定义ContentProvider时的authority为key来保存当前userId下正在运行的ContentProvider对应的ContentProviderRecord结构。
      mProvidersByClassPerUser:
      先用userId做一级分类,再在每一个userId中以ContentProvider对应的ComponentName为key来保存当前userId下正在运行的ContentProvider对应的ContentProviderRecord结构。
      简而言之,前二者中只保存跨用户单例(即singleton为true)的ContentProviderRecord结构。后二者中只保存不跨用户单例(即singleton为false)的ContentProviderRecord结构。

    3. ContentProviderRecord的connections成员是一个容器,保存了所有访问该ContentProvider的客户端的连接。

    4. 整个ContentProvider的管理都是动态的,也就是说ProviderMap中保存的都是正在运行的ContentProvider对应的ContentProviderRecord结构,所谓正在运行其实就是指有被客户端访问的ContentProvider。所以说当ContentProvider所在进程挂掉时,其对应的ContentProviderRecord结构也会从ProviderMap中删除,当有客户端访问时又会添加进去。同样ContentProviderConnection也一样是动态管理的。

    加料

    可以用adb shell dumpsys activity providers命令将当前手机系统中运行的ContentProvider信息dump出来:
    adb shell dumpsys activity providers

    ACTIVITY MANAGER CONTENT PROVIDERS (dumpsys activity providers)
      Published single-user content providers (by class):
      * ContentProviderRecord{6da87f4 u0 com.android.providers.settings/.SettingsProvider}
        package=com.android.providers.settings process=system
        proc=ProcessRecord{1c0b52f 907:system/1000}
        uid=1000 provider=android.content.ContentProvider$Transport@5e045f5
        singleton=true
        authority=settings
        isSyncable=false multiprocess=false initOrder=100
        Connections:
          -> 1366:com.google.android.inputmethod.pinyin/u0a90 s1/1 u0/0 +1d5h33m4s312ms
          -> 1382:com.android.systemui/u0a39 s1/1 u0/0 +1d5h33m4s249ms
          -> 1506:com.quicinc.cne.CNEService/1000 s1/1 u0/0 +1d5h33m3s747ms
          -> 1540:com.android.phone/1001 s1/1 u0/0 +1d5h33m3s666ms
          -> 2143:com.google.android.apps.nexuslauncher/u0a61 s1/1 u0/0 +1d5h32m51s663ms
          -> 2061:com.android.ims.rcsservice/1001 s1/1 u0/0 +1d5h32m51s634ms
          -> 2049:com.android.nfc/1027 s1/1 u0/0 +1d5h32m51s523ms
          -> 2032:com.google.android.googlequicksearchbox:interactor/u0a47 s1/1 u0/0 +1d5h32m50s742ms
          -> 2763:com.android.bluetooth/1002 s1/1 u0/0 +1d5h32m45s175ms
          -> 2852:com.tencent.mm:exdevice/u0a129 s1/1 u0/0 +1d5h32m44s534ms
          -> 2586:com.google.android.gms/u0a40 s1/1 u0/0 +1d5h32m41s651ms
          -> 3680:cn.com.langeasy.LangEasyLrc:channel/u0a143 s1/1 u0/0 +1d5h32m24s937ms
          -> 8803:cn.com.langeasy.LangEasyLexis/u0a152 s1/1 u0/0 +1d5h28m37s335ms
          -> 4741:com.google.android.googlequicksearchbox:search/u0a47 s1/1 u0/0 +4h54m32s412ms
          -> 5436:com.tencent.mm:push/u0a129 s1/1 u0/0 +4h54m4s981ms
          -> 14262:com.google.android.gms.persistent/u0a40 s1/1 u0/0 +3h40m12s345ms
          -> 16671:cn.com.langeasy.LangEasyLexis:channel/u0a152 s1/1 u0/0 +3h23m10s31ms
          -> 20077:com.google.android.tts/u0a84 s1/1 u0/0 +3h20m16s488ms
          -> 20364:cn.poco.interphoto2/u0a138 s1/1 u0/0 +3h20m1s501ms
          -> 20480:com.tencent.mm/u0a129 s1/1 u0/0 +3h19m59s219ms
          -> 27828:cn.com.langeasy.LangEasyLrc/u0a143 s1/1 u0/0 +2h18m39s482ms
          -> 32580:com.vip.zb:pushcore/u0a201 s1/1 u0/0 +2h9m13s53ms
          -> 31904:android.process.media/u0a9 s1/1 u0/0 +2h2m58s536ms
          -> 1866:com.google.android.apps.photos/u0a116 s1/1 u0/0 +1h58m55s281ms
          -> 3892:com.tencent.mm:appbrand0/u0a129 s1/1 u0/0 +1h51m37s766ms
          -> 32527:com.vip.zb/u0a201 s1/1 u0/0 +1h0m10s366ms
          -> 9189:com.android.vending/u0a59 s1/1 u0/0 +58m1s728ms
          -> 11603:com.evernote/u0a135 s1/1 u0/0 +2m7s712ms 
        ---此处省略一堆ContentProviderRecord---
    
      Published user 0 content providers (by class):
      * ContentProviderRecord{3835601 u0 com.google.android.gms/.auth.api.credentials.be.persistence.TemporaryValueProvider}
        package=com.google.android.gms process=com.google.android.gms
        proc=ProcessRecord{ea68992 2586:com.google.android.gms/u0a40}
        uid=10040 provider=android.content.ContentProviderProxy@a2fbf8c
        authority=com.google.android.gms.auth.api.credentials.be.persistence.TemporaryValueProvider
        ---此处省略一堆ContentProviderRecord---
    
      Single-user authority to provider mappings:
      settings: 6da87f4/com.android.providers.settings/.SettingsProvider
      ---此处省略一堆name: ComponentName---
    
      User 0 authority to provider mappings: 
      com.google.android.gms.auth.api.credentials.be.persistence.TemporaryValueProvider: 3835601/com.google.android.gms/.auth.api.credentials.be.persistence.TemporaryValueProvider
      ---此处省略一堆name: ComponentName---
     
      
    

    由于系统中运行的ContentProvider太多,这里的dump结果我省略了做了缩减。它的内容其实很简单,就是对ProviderMap中的四个集合做遍历并dump信息。整个dump内容也因此也分为四大段。
    简要说明:

    • Published single-user content providers (by class):
      mSingletonByClass集合中的记录。
    • Published user 0 content providers (by class):
      mProvidersByClassPerUser集合中的记录。
    • Single-user authority to provider mappings:
      mSingletonByName集合中的记录。
    • User 0 authority to provider mappings:
      mProvidersByNamePerUser集合中的记录。
    • 上面的例子中看到ComponentName为com.android.providers.settings/.SettingsProvider的ContentProvider打印出的Connections就是对应于ContentProviderRecord中的connections成员。可以看到系统中访问设置的ContentProvider的客户端有很多。
    • ByClass的集合dump的是每个ContentProviderRecord的具体信息。ByName的集合dump的是key: value形式,其中key是定义该ContentProvider时指定的authority,value是该描述该ContentProvider的ComponentName。
    • 具体每个字段的含义可以直接参考ProviderMap的源码,这里就不一一解释了。

    And Then

    后面我会再根据自己的理解总结一篇ContentProvider基本运行流程的文章,同样以一张图的形式尽量简单的表达出其内部的基本思想。

    Thank you~

    相关文章

      网友评论

        本文标题:一图解惑之Android管理ContentProvider结构

        本文链接:https://www.haomeiwen.com/subject/ikhonxtx.html