- Android进阶解密
- 刘望舒
- 1598字
- 2020-08-27 17:04:56
4.5 Content Provider的启动过程
Content Provider作为四大组件之一,即内容提供者,在通常情况下并没有其他的组件使用频繁,主要用于进程内和进程间的数据共享。Content Provider的启动过程分为两个部分来进行讲解,分别是query方法到AMS的调用过程和AMS启动Content Provider的过程。
4.5.1 query方法到AMS的调用过程
为了便于理解Content Provider的启动过程,首先列出一段使用Content Provider的代码,如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer111.jpg?sign=1739631563-3PPwB5dimKLY927tI95EMnm81GGVqEEZ-0-da916ee8189820975a04ebed35a62754)
在ContentProviderActivity中,我们在GameProvider中插入了一条数据,可见要想插入一条数据,或者说使用ContentProvider,需要先调用getContentResolver方法,如下所示:这里的mBase已经是我们的老朋友了,指的是ContextImpl,ContextImpl的getContentResolver方法如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer112.jpg?sign=1739631563-SGk7ZABwC5IUYNhYaDCWpItinG2SZP7h-0-318fc0bae4155d912670fa563f0eaa0b)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer113.jpg?sign=1739631563-g9SxJyjNXwrgEDLBedouaCTR0R0yKc39-0-e65750a49f4f79df555aedbe57c5670e)
getContentResolver方法中返回了ApplicationContentResolver类型的mContentResolver对象,ApplicationContentResolver是ContextImpl中的静态内部类,继承自ContentResolver,它在ContextImpl的构造方法中被创建,这说明当我们调用ContentResolver的insert、query、update 等方法时就会启动Content Provider。这里以query方法来进行举例,query方法在ApplicationContentResolver的父类ContentResolver中实现,有3个重载方法,最终会调用如下的query方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer114.jpg?sign=1739631563-BCIXyLW47pSIwXu0hNW1RYyX2BYPc6b2-0-0a27ce0c621d55b7d4a5da31fdedfbab)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer115.jpg?sign=1739631563-rHIyxcdmco1PDsBF5TmgNH0HY4ftQPG8-0-bc63349836ea318315b62f05b817abd6)
在注释1处通过acquireUnstableProvider方法返回IContentProvider类型的unstableProvider对象,在注释2处调用unstableProvider的query方法。IContentProvider是ContentProvider 在本地的代理,具体的实现为ContentProvider,我们查看ContentProvider的acquireUnstableProvider方法做了什么,如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer116.jpg?sign=1739631563-VK5dZGBODAMVRQ4mdoDKNiFfR2j3rNRs-0-751e877a74a957a807f7b494d24daf12)
在注释1处检查uri的scheme是否等于“content”(SCHEME_CONTENT的值为“content”),如果不是则返回null。在注释2处调用了acquireUnstableProvider方法,这是个抽象方法,它在ContentResolver的子类ApplicationContentResolver中实现,ApplicationContentResolver是ContextImpl的静态内部类,如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer117.jpg?sign=1739631563-UuqJXXSYKq686PfxpXmGjczTcajwlyzy-0-916df8b12afe0e893c8397a40de3bb0a)
在acquireUnstableProvider方法中返回了ActivityThread类型的mMainThread对象的acquireProvider方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer118.jpg?sign=1739631563-rfV3CjDlajqAh2fofifqGLtXWQpL23b3-0-ab39b36291636be53efcc46568093471)
注释1处的acquireExistingProvider 方法内部会检查ActivityThread的全局变量mProviderMap中是否有目标ContentProvider 存在,有则返回,没有就会在注释2处调用IActivityManager的getContentProvider方法,最终会调用AMS的getContentProvider方法。注释3处的installProvider 方法用来安装ContentProvider,并将注释2处返回的ContentProvider 相关的数据存储在mProviderMap中,起到缓存的作用,这样使用相同的Content Provider 时,就不需要每次都要调用AMS 的getContentProvider 方法了。接着查看AMS的getContentProvider方法,代码如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer119.jpg?sign=1739631563-roArgedl2tz8qzBw4HF85QvvVDxvtBoh-0-9d301effd93802f9afafe171d1e3b33a)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer120.jpg?sign=1739631563-VrUWmQTj4UXh6fRb5BM8O1jkrtY8AdG3-0-c575b9765f7e903a190f26d2936cfd45)
getContentProvider方法返回了getContentProviderImpl方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer121.jpg?sign=1739631563-eB6seyeHyBw6iZoIJk4Uz0jFSiAS1uIt-0-8dfc61296db9655f1bd7fa12b64ddb90)
getContentProviderImpl方法的代码很多,这里只截取了关键的部分。在注释1处通过getProcessRecordLocked方法来获取目标ContentProvider的应用程序进程信息,这些信息用ProcessRecord类型的proc来表示,如果该应用程序进程已经启动就会调用注释2处的代码,否则就会调用注释3处的startProcessLocked方法来启动进程。此前我们都假设应用程序进程已经启动的情况,这里假设ContentProvider的应用程序进程还没有启动,应用程序进程启动最终会调用ActivityThread的main方法,不了解的读者请查看本书第2章的内容。ActivityThread的main方法如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer122.jpg?sign=1739631563-ZFtUn1lRT8hWUCe5CUwFBoG8R22WJolm-0-1dc62459852d09e447119ff6cb42091e)
在注释1处通过prepareMainLooper方法在ThreadLocal中获取Looper,并在注释3处开启消息循环。在注释2处创建了ActivityThread,紧接着调用了它的attach方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer123.jpg?sign=1739631563-ZjIocs0IomK8vKoE3XI37gLG66Cbur5T-0-f2992ed874b505424d9a3c04e96f2008)
注释1处得到IActivityManager,在注释2处调用IActivityManage的attachApplication方法,并将ApplicationThread类型的mAppThread对象传进去,最终调用的是AMS的attachApplication方法。query方法到AMS的调用过程(省略应用程序进程启动过程)的时序图如图4-15所示。
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer124.jpg?sign=1739631563-Fn0LA3Teju4QJxqKJ39ISS7wIdtllN3b-0-598655b89c3b49f35dab023efc48054a)
图4-15 query方法到AMS的调用过程的时序图
4.5.2 AMS启动Content Provider的过程
AMS启动Content Provider的时序图如图4-16所示。
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer126.jpg?sign=1739631563-8BcQuP0X1TCiqEKPsJU9gZwIqSdbktbq-0-a268a37aee9ba71aa1c99f9a2acf9bf7)
图4-16 AMS启动Content Provider的过程的时序图
我们接着来查看AMS的attachApplication方法,如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer125.jpg?sign=1739631563-uSQsVIO9s8EPmre9vyyHRTqlZMJrlqtH-0-36d26b5fab4d5ca989357455034b7735)
在attachApplication方法中又调用了attachApplicationLocked方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer127.jpg?sign=1739631563-0Gt92DVQpJcPp7U1MhdrQL2sw45g1u29-0-8bb9ed474c515cb9dac0235a4d18713e)
在attachApplicationLocked方法中调用了thread的bindApplication方法,thread是IApplicationThread类型的,这里和IActivityManager一样采用了AIDL,实现bindApplication方法的不再是Android 7.0的ApplicationThreadProxy类,而是ApplicationThread类,它是ActivityThread的内部类,如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer128.jpg?sign=1739631563-eWecGMfUtyoNqeiA4hgKkMgPybQXba7W-0-a0d6691983a4b1bf8af0511a77b7856f)
在bindApplication方法中最后调用sendMessage方法向H发送BIND_APPLICATION类型消息,H的handleMessage方法如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer129.jpg?sign=1739631563-6pNQ0itZK1tNMCY1DsGRR9SVl17hN5dw-0-e21d053b204372c255c98590e65e1158)
我们接着查看handleBindApplication方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer130.jpg?sign=1739631563-cvuubgreGFQBBF5F9nE9Y390QhN5BkG1-0-03c5a8af3f9d52d893915806e08e4e92)
handleBindApplication 方法的代码很长,这里截取了主要的部分。在注释1处创建了ContextImpl。在注释2处通过反射创建Instrumentation并在注释3处初始化Instrumentation。在注释4处创建Application并且在注释6处调用Application的onCreate方法,这意味着Content Provider 所在的应用程序已经启动,在应用程序启动之前,在注释5处调用installContentProviders方法来启动Content Provider,代码如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer131.jpg?sign=1739631563-hhrDvLR8dG6XdK3UTkskUKN6CcXaRWO9-0-2f9f9d18cf8ba93f95909a510b3807f5)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer132.jpg?sign=1739631563-z3XTXvf942F50tkI6qMqibJQb6KqzECr-0-cf117ad18661cdbb5df253602fa80181)
在注释1处遍历当前应用程序进程的ProviderInfo列表,得到每个Content Provider的ProviderInfo(存储Content Provider的信息),并在注释2处调用installProvider方法来启动这些Content Provider。在注释3处通过AMS的publishContentProviders方法将这些Content Provider存储在AMS的mProviderMap中,这个mProviderMap在前面提到过,起到缓存的作用,防止每次使用相同的Content Provider时都会调用AMS的getContentProvider方法。下面来查看installProvider方法是如何启动Content Provider的,installProvider方法如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer133.jpg?sign=1739631563-i5bvu5EtccG0PoLvcsgBqeRLmayIohMh-0-4fd7419f7bc8f9e8a1d5333932c6b283)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer134.jpg?sign=1739631563-4P4PhnzKcgChUfxE9ithfimpUrMXbuSd-0-db5fdd6497504689f31ce6b12138bfd4)
在注释1处通过反射来创建ContentProvider类型的localProvider对象,并在注释2处调用了它的attachInfo方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer135.jpg?sign=1739631563-bOg0YuamGlTuhncSNwyYRWmQy3Ddd4aT-0-dd5ff41dd853298079dc1d412133cd0a)
在attachInfo方法中调用了onCreate方法,它是一个抽象方法,这样Content Provider就启动完毕。当然这只是Content Provider启动过程的一个分支,即应用程序进程没有启动的情况,还有一个分支是应用程序进程已经启动的情况,这就需要读者自行阅读源码了。