![Android进阶解密](https://wfqqreader-1252317822.image.myqcloud.com/cover/331/31186331/b_31186331.jpg)
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=1738969179-H7d74DMirQOCFtN19hQzS33YT9SF1M0n-0-f79d4705e430a78902e311955a8b1d02)
在ContentProviderActivity中,我们在GameProvider中插入了一条数据,可见要想插入一条数据,或者说使用ContentProvider,需要先调用getContentResolver方法,如下所示:这里的mBase已经是我们的老朋友了,指的是ContextImpl,ContextImpl的getContentResolver方法如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer112.jpg?sign=1738969179-KQ5gtARSoFM0bkpWCdXCd4F0Jtvj8DoW-0-332af822a393f5fb182d73baa7e7b509)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer113.jpg?sign=1738969179-37FJ1UnM2ee5qbSwTcnCYZjPVr2SWRI5-0-49e608692592ec2a95c0a92b04bdd055)
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=1738969179-cyuTwfx5pvDurpYYQ3EmEyPJCvJfYgnT-0-582274568d699643819d268612bb667b)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer115.jpg?sign=1738969179-7BJ2xrjm5jMHtH41MPPex8kYpxaZ3wQo-0-c8cf4d03182989bcc72a28b4ed05cd36)
在注释1处通过acquireUnstableProvider方法返回IContentProvider类型的unstableProvider对象,在注释2处调用unstableProvider的query方法。IContentProvider是ContentProvider 在本地的代理,具体的实现为ContentProvider,我们查看ContentProvider的acquireUnstableProvider方法做了什么,如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer116.jpg?sign=1738969179-nAuIMPF6gobf7DeSWij1oHEH5MeLApte-0-78545758a19f01751f06a9bbbc3b4ef7)
在注释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=1738969179-f3nFMeylYYHQqWj5mhGvFGffcy3RUxN1-0-4d332ba10c78f725fa30d2b5ee2243e1)
在acquireUnstableProvider方法中返回了ActivityThread类型的mMainThread对象的acquireProvider方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer118.jpg?sign=1738969179-iJuoIbbwwzzkA2TUxPB4lmVoEWqBdihw-0-c7b08e22c6765556d8444626fe6883e0)
注释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=1738969179-oDcYcDR51X5FmIZ7o1VGstHRxr0SWF88-0-fcdba4b3c93a92d02db23d768669a7ac)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer120.jpg?sign=1738969179-q5FbHJH8HILxi5JpnsZyVIwZRmVFwWRX-0-b6a9e638a122c95c676d6764ad14d60b)
getContentProvider方法返回了getContentProviderImpl方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer121.jpg?sign=1738969179-OsbMDQCldKXdypi4oVB5XKmfho8B1krd-0-75445784d49f48e9e7a22e8e5278a389)
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=1738969179-AyU0phAExmYx9nbqXIrodbPEIT8I8RN2-0-387226b6d540b6fbf1253fee0eb996ab)
在注释1处通过prepareMainLooper方法在ThreadLocal中获取Looper,并在注释3处开启消息循环。在注释2处创建了ActivityThread,紧接着调用了它的attach方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer123.jpg?sign=1738969179-LxJiT9i6tS1Jaw1OrUCHjKG1HPKwbNqh-0-9c6a037fcb3e4d1bc1c78cab441356e4)
注释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=1738969179-BYXPkRT4iItfEjdFBrnRRa9MFUOQkdgB-0-12f3af2410d21e7da7545bd6de611087)
图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=1738969179-OQRgx5EOAocQgBABQT8Yy8ylg8j2pQ8S-0-d5336ed31302f24463c18633735eabd3)
图4-16 AMS启动Content Provider的过程的时序图
我们接着来查看AMS的attachApplication方法,如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer125.jpg?sign=1738969179-WZhPo6J9SgS0le98l5HX9gv9fKFRMo5T-0-a216c869ac45a270a972b6b022900d0c)
在attachApplication方法中又调用了attachApplicationLocked方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer127.jpg?sign=1738969179-KUC1PHlnlw5OS32z4WTK3eY4VMfCpcHp-0-ac247b458062c9f434e05a1586f6daa5)
在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=1738969179-O6qlHVU3hLBau3HpJ7U9xX65zOdhtkoz-0-cf0a0155d0839af33e8ff3bc3e551461)
在bindApplication方法中最后调用sendMessage方法向H发送BIND_APPLICATION类型消息,H的handleMessage方法如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer129.jpg?sign=1738969179-nLV94aPjWT0lISEDh5Why5RjDHf9xFuA-0-33983bf45a40dfc4fcded1352cd332ee)
我们接着查看handleBindApplication方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer130.jpg?sign=1738969179-TDoQ1N47FKlZxueRgBSu26nftjySz3IB-0-a827010fa07e446b48e7a3c3cfbd8420)
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=1738969179-dCyV6Sf2x031BjnD1qb0KCSjhsEsGifu-0-22266280a3ebc6ca4862a711904dccdf)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer132.jpg?sign=1738969179-xy2AsNYjhST8kZQ7vCsYvRXaLRkL2VP3-0-80488b144e21a42f8283dc83255a7d7e)
在注释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=1738969179-GhACWyJNoVFGOjOMxoMMcYrsM1tNdqqA-0-637b58ae7051b0ae89c7424ec7b11de8)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer134.jpg?sign=1738969179-DZTVWRYBvlN4QkQ6TMsF37knIqqxehYa-0-67a791387835ae606157553a5be436b6)
在注释1处通过反射来创建ContentProvider类型的localProvider对象,并在注释2处调用了它的attachInfo方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer135.jpg?sign=1738969179-CWbpZmGvB4u3iBBsWjGukjGr3caLi8Ow-0-ee6b5f800d97a2d778008306bdfa2e7b)
在attachInfo方法中调用了onCreate方法,它是一个抽象方法,这样Content Provider就启动完毕。当然这只是Content Provider启动过程的一个分支,即应用程序进程没有启动的情况,还有一个分支是应用程序进程已经启动的情况,这就需要读者自行阅读源码了。