一图解惑之Android调用ContentProvider基本流程

准备

  • 基本使用
    ContentProvider作为四大组件之一,在开发过程中经常被使用到。我们的常规做法是定义一个ContentProvider,然后在使用的时候使用ContentResolver提供的接口来访问数据。一个进程可以访问自己定义的ContentProvider,也可以访问其他进程定义的ContentProvider。
  • ContentProviderHolder:
    ContentProviderHolder是定义在IActivityManager类中的内部类,它的作用主要是承载ContentProvider的信息和ContentProvider接口内容,并在SystemServer和App进程间传递。其主要成员变量如下:
    1. info :ProviderInfo 描述该ContentProvider的信息。
    2. provider :IContentProvider 描述该ContentProvider对应的ContentProvider接口,它是一个Binder代理对象。
  • 大致流程:假设App B定义了一个ContentProvider,App A需要访问该ContentProvider。整个调用过程简单梳理会经过以下几个步骤:
    1. App A先向SystemServer查询要访问的ContentProvider接口。
    2. SystemServer将查询到的ContentProvider接口返回给App ,该ContentProvider接口是定义在App B中的ContentProvider的Binder代理。
    3. App A再通过该Binder代理调用定义在App B中的ContentProvider的逻辑。
  • 本地缓存:ActivityThread内部会缓存当前进程已经访问过的ContentProvider或者是当前进程内自己定义的ContentProvider。缓存在其成员变量mProviderMap中。这里注意参考上一篇一图解惑对比区分,在AMS中也有个同名的成员变量mProviderMap,可以大致理解为他们都用来缓存ContentProvider接口,只不过一个是在本地进程中缓存,一个是在SystemServer中全局缓存。

上图

这次不墨迹,先上图再说:


Provider时序图 (1).png

查看原图

图中每个步骤都标了序号,整个过程涉及到3个进程间的通信。App A是ContentProvider的使用方,App B是ContentProvider的定义方,SystemServer是系统进程。下面就按序号顺序做简要说明。
简要总结:
1.业务逻辑层通过直接使用ContentResolver提供的接口去访问数据(如query,insert等等)。
2.ContentResolver需要获取到ContentProvider接口才能对该ContentProvider访问,所以这里ContentResolver先通过ActivityThread去获取一个ContentProvider接口。
3.上面提到ActivityThread的mProviderMap成员会缓存已经获取的ContentProvider接口或定义在自己进程内的ContentProvider接口。所以在这里ActivityThread会先通过acquireExistingProvider()方法在本地查找是否已有缓存。如果有,则直接跳到第13步返回结果给ContentResolver。如果没有则会继续通过进程间通信向SystemServer中的AMS模块查询ContentProvider接口。
4.向AMS查询ContentProvider接口。在AMS的mProviderMap成员中保存了当前运行的ContentProvider接口,查询过程中大方向会有两个分支:如果要查询的ContentProvider接口不在mProviderMap缓存中,则一般情况下也说明该ContentProviders所在的进程还没启动,则继续往下执行第5步启动该进程。否则直接跳到第12步,将从mProviderMap成员中查询到的结果返回给ActivityThread。
5.启动要查找的ContentProvider所在的进程。接下来便是大家熟知的应用进程启动的过程。这里主要关注第10步和第11步。
6~9.应用进程启动初始化过程中与AMS的一系列交互。
10.通过调用installContentProviders()方法本地安装ContentProvider,这里可以简单理解所谓本地安装就是填充ActivityThread中保存ContentProvider接口的缓存,但是要注意当前的ActivityThread是表示定义ContentProvider的进程(App B),而不是使用ContentProvider的进程(App A)。
11.回调AMS的publishContentProviders()方法,将ContentProvider接口传给AMS。至此AMS内部的mProviderMap成员也获取到了ContentProvider接口并保存。当下次有进程再请求访问同一个ContentProvider接口时,就可以直接从AMS的mProviderMap成员中获取该ContentProvider接口直接返回给调用方了。
12.返回查询结果给ContentProvider的使用方(App A)。这里要注意返回的数据类型是ContentProviderHolder,它继承了Parcelable接口。因此可以在进程间传递,使用方需要的ContentProvider接口就保存在其成员变量provider中。
13.ContentProvider的使用方将查找回来的ContentProvider接口返回给ContentResolver。
14.ContentResolver通过ContentProvider接口再次发起Binder通信向真正ContentProvider的实现方(App B)发送数据访问请求。
15.ContentProvider实现方(App B)将数据返回给Content使用方(App A)。
16.ContentResolver完成数据的访问工作,将查询结果返回给业务逻辑层。

整个过程有可能只涉及一个进程(App A),比如以下几种情况:

  • ContentProvider的使用方(App A)已经有要访问的ContentProvider接口的缓存。
  • ContentProvider的定义与使用在相同的进程。

也可能涉及两个进程(App A和SystemServer):

  • ContentProvider的定义与使用在不同的进程,但是AMS中已经有该ContentProvider接口的缓存。

也可能涉及三个进程(App A,App B和SystemServer):

  • ContentProvider的定义与使用在不同的进程,并且AMS中也没有该ContentProvider接口的缓存。

And Then

我的分析默认是按照Android N原代码的逻辑来分析的。如果在国产机上发现逻辑与之有冲突不要惊慌,国产机ROM在AOSP基础上做了很多“优化”。或者不同的Android版本之间可能也存在微小差别。比如我的测试过程中就发现手上一台华为在App B定义了ContentProvider,但是App B进程还没有启动时,App A使用该ContentProvider并不会把App B拉起,而是抛一个Warning:Unknown URL content://xxxxxxxxxxxxx
四大组件中之所以没有先写Activity和Broadcast是因为网上他们哥两的介绍相对多的多。不过后面我也会抽空把一图解惑系列的四大组件都补全。一图解惑系列的目的是希望尽量简单直接的将基本的原理逻辑说清楚,如果需要了解内部细节,当然还是需要Read The Fucking Source Code。

Thank you~

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

推荐阅读更多精彩内容

  • AndroidSmall框架是android中一个非常优秀的插件化框架,有时我们不仅要学会使用它,也要去理解它的原...
    柴犬大人阅读 1,240评论 0 6
  • 没有勤于笔耕,是我对自己的批评。虽然我仍然追求精神上的充盈,但是疏于记录却是自我的放纵,这是2017年年终总结首先...
    悠游四海阅读 282评论 6 5
  • 第一次见到杨海,是2013年的时候了。那年春天,我依父母之命回家相亲。母亲大人一直在我耳边说男方多好多好,某某名校...
    老狼叔叔阅读 5,705评论 1 4
  • 喜欢你心无旁骛的样子。 这是名为花儿app上的主题语。软件上的花画得朴素而美丽,带着孩童稚嫩的视角描摹着每朵意义不...
    虚谷沉香阅读 452评论 1 1
  • python允许函数从调用语句中收集任意数量的实参。 大大: 函数就好比是你厌倦了重复差不多内容的功能,这些功能都...
    三千院贺Hall阅读 343评论 1 0