![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=1739196427-Yawu1JuzomrRiiEoa8HBdHA6s69SALOM-0-17087cd681632eb665e07a36e0a2a07a)
在ContentProviderActivity中,我们在GameProvider中插入了一条数据,可见要想插入一条数据,或者说使用ContentProvider,需要先调用getContentResolver方法,如下所示:这里的mBase已经是我们的老朋友了,指的是ContextImpl,ContextImpl的getContentResolver方法如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer112.jpg?sign=1739196427-piEncEwYljORTIVb9JREtdzEroUp6YoB-0-b5ce77c4f9f550dcd5045b06ee4a6ccf)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer113.jpg?sign=1739196427-qtYyPlWJ4GFqBGAheqV6ho0rPH9Z1k6f-0-5fee7cd0b72db1a488c4370180a0c91a)
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=1739196427-IDwBuVwBXf4tW82rQU5vDX3qUZHQis6Z-0-ea961b29f0e15ab5931f44bcc2f9edc6)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer115.jpg?sign=1739196427-goXibE4TuZuFuWTSrCfQHZM97SFGygwI-0-b39927567e9036436752b8570c8e24c4)
在注释1处通过acquireUnstableProvider方法返回IContentProvider类型的unstableProvider对象,在注释2处调用unstableProvider的query方法。IContentProvider是ContentProvider 在本地的代理,具体的实现为ContentProvider,我们查看ContentProvider的acquireUnstableProvider方法做了什么,如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer116.jpg?sign=1739196427-TjY1NzHOfeKm7PtYdHK7ZGGVgrQOMaNl-0-0c991b524528c598a1857f839b98e54f)
在注释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=1739196427-sATph96Cp4PImiiC0AnJlxC5qHQ8vi4a-0-d3ba407f83b95666b06ad0071213ddec)
在acquireUnstableProvider方法中返回了ActivityThread类型的mMainThread对象的acquireProvider方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer118.jpg?sign=1739196427-IrHGXaR7L6jrEJevuDSabC1nP72pP6Ho-0-128f01e443a2b27a20ab10222362827d)
注释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=1739196427-tuNepDP3ja5AUZkRRhYnOYGWLswI8Sa7-0-8737f57ef82170cb7cd160c08a30f799)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer120.jpg?sign=1739196427-ITnCuVe2RCpgeZIfFAL84Lvy7gf9uq0q-0-bcffaafd7977b2237d6531aeba5742ae)
getContentProvider方法返回了getContentProviderImpl方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer121.jpg?sign=1739196427-ZsUmRTrJd34A37Pz8PbC2E7004zqOkqY-0-5a9e725b4e3e9e61359b7dac020eee51)
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=1739196427-8WWB6CAc9OwNfrlByimswzJeDg3VsOvH-0-ae426af7209e6ab8c2b5587254c6209d)
在注释1处通过prepareMainLooper方法在ThreadLocal中获取Looper,并在注释3处开启消息循环。在注释2处创建了ActivityThread,紧接着调用了它的attach方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer123.jpg?sign=1739196427-2HEdvTXpLUjmPEGz1bC058mqTllf6GSl-0-b851176d17a70e92c8a08e6b1378c612)
注释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=1739196427-OmIntCTJcmDISNAk7sd2GDjDP6iaDztS-0-f93e5e6b4f4f0e7f9018779a67d54628)
图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=1739196427-aOJP5rdMhuKwgBblZAQ72OYC2ESvOTCk-0-4584383913deb755d386c748ede6b0b1)
图4-16 AMS启动Content Provider的过程的时序图
我们接着来查看AMS的attachApplication方法,如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer125.jpg?sign=1739196427-8tBAfMrmu2bnfoUhQOdx6UYbN0GUCACj-0-270bff62305a63b6bb0f32da239a4859)
在attachApplication方法中又调用了attachApplicationLocked方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer127.jpg?sign=1739196427-WnqPQMUp3Km0nj1i8KKZX58Z1lZ1vmuU-0-74679aa79137514fed02b1d6e9f8dbbd)
在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=1739196427-0qIn42kmEOcaxIlA93vcduXbV5Q5utiw-0-b044451009b571da7b02b365430380d6)
在bindApplication方法中最后调用sendMessage方法向H发送BIND_APPLICATION类型消息,H的handleMessage方法如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer129.jpg?sign=1739196427-Pl9LWycuxLUPAvvv3TSbmU3FwexMZl37-0-66f693bf5d81f43fd9641c765c8559c0)
我们接着查看handleBindApplication方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer130.jpg?sign=1739196427-isWg2AORcO1UbCpsqTbM9eVYssur4sA0-0-53a1559a25f53c118d10c1d954581736)
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=1739196427-aXxtC24hb3EF9ut3mW4O8vFP6TxY1ZSc-0-3c3aaab9440da6441b918635b8eeeaae)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer132.jpg?sign=1739196427-a1pYS4HsjSl78XjIvIQfaNxScsLU7xaO-0-8aef8c2fda840b5a3be1a2996bb04a3a)
在注释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=1739196427-sUhT396qPZo5XQ8qTwFdYwbccTxepmtD-0-e3f9506236fbb9a4934da1d40adf713d)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer134.jpg?sign=1739196427-wdqoGJeQrIh8VTIbp31flPOqENldMt5V-0-1b8bad5342fdbea04a560c378f3c8df4)
在注释1处通过反射来创建ContentProvider类型的localProvider对象,并在注释2处调用了它的attachInfo方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer135.jpg?sign=1739196427-tCbl9HTIpgsmmWnaJR8nXfSSjSNZd4LS-0-06694fb04c7be4772d105716a8e52744)
在attachInfo方法中调用了onCreate方法,它是一个抽象方法,这样Content Provider就启动完毕。当然这只是Content Provider启动过程的一个分支,即应用程序进程没有启动的情况,还有一个分支是应用程序进程已经启动的情况,这就需要读者自行阅读源码了。