一图解惑之Android管理ContentProvider结构

准备

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

在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~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,646评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,979评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,391评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,356评论 0 215
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,740评论 3 293
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,836评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,022评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,764评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,487评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,728评论 2 252
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,200评论 1 263
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,548评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,217评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,134评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,921评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,919评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,766评论 2 274

推荐阅读更多精彩内容