![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=1739302204-pV67HsRHWTbjOHUKHm26QPBJ6ceamHOE-0-400c231ad1faa8073e57a3ba58e8f17f)
在ContentProviderActivity中,我们在GameProvider中插入了一条数据,可见要想插入一条数据,或者说使用ContentProvider,需要先调用getContentResolver方法,如下所示:这里的mBase已经是我们的老朋友了,指的是ContextImpl,ContextImpl的getContentResolver方法如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer112.jpg?sign=1739302204-C5dqSgx60RzVpKwOccZhX1s9pznWdAGP-0-4c0357af50dcd6fd05753e60e82d805b)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer113.jpg?sign=1739302204-cU06Rs6jd6JT4V2qkwwlK0sn3u4iXxN0-0-5f05dcfbcba64a5c6c5ba5310aed1d27)
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=1739302204-S3JUzYxL1Dk6tqFGGIT91aCQnJwUb0E6-0-c7079c3d31f8dc58192f8ceb5a4d4393)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer115.jpg?sign=1739302204-Jo5tEsFOgVmFFXdruKIlvCLiYDOKHwuD-0-ec117b5c98527aad6ab1b48e2ae441bd)
在注释1处通过acquireUnstableProvider方法返回IContentProvider类型的unstableProvider对象,在注释2处调用unstableProvider的query方法。IContentProvider是ContentProvider 在本地的代理,具体的实现为ContentProvider,我们查看ContentProvider的acquireUnstableProvider方法做了什么,如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer116.jpg?sign=1739302204-8ZJ5bAVlQugM8G1njpRwMnTtqlg3gNgU-0-28d738e78aa48f98061e9822b2c082f9)
在注释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=1739302204-Avbs3z4GnXLNyFXglL5y6heaZQPy2rOT-0-bb0ca9897aff64628d99b6760ffd5945)
在acquireUnstableProvider方法中返回了ActivityThread类型的mMainThread对象的acquireProvider方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer118.jpg?sign=1739302204-kREX4IXSzok09xarq6KFdZDS6eGIEdRq-0-9d05302fb5e09bf5728dfcd692537e87)
注释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=1739302204-GO2S1YvLupRSBzuXiXrc5fSfNxTXymEc-0-426212d53af80ca4e4c37cf4406bbbdb)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer120.jpg?sign=1739302204-3e783WuqZsAQeOTnwlj38v0w9uBRhtaa-0-e6db41e290089e52b94e5c88ba91afbd)
getContentProvider方法返回了getContentProviderImpl方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer121.jpg?sign=1739302204-ZkExYNDoBwe67rqfQ8WqI1afVSp0eNCs-0-9cc890b0ba5ecfa6ca3babbdd0a91ca4)
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=1739302204-WKpcesLpVWuVTZ7C1oX6I2dBdeCZ7nOr-0-92b7853628a3550ed7637b046fd2a8ea)
在注释1处通过prepareMainLooper方法在ThreadLocal中获取Looper,并在注释3处开启消息循环。在注释2处创建了ActivityThread,紧接着调用了它的attach方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer123.jpg?sign=1739302204-ayGwmV0ClsTBNcrHIxzG4ky7Czlxk1SS-0-f46e789149326c3613fad474a16f4f69)
注释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=1739302204-D0mBx4cvBpl83NlGdOX376bhSEJIcxdu-0-c58cae18500a5524335f65113687b32d)
图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=1739302204-AkEs5eISmPpUTZ2tFsZrwEefD2Q6xPNy-0-7a1297ea48a87ca39601ca98d29d6645)
图4-16 AMS启动Content Provider的过程的时序图
我们接着来查看AMS的attachApplication方法,如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer125.jpg?sign=1739302204-hlQcFpJvbGz90easYmoZotXhvd7CJjco-0-8fb32b325291eb09786c0fc8aac291ad)
在attachApplication方法中又调用了attachApplicationLocked方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer127.jpg?sign=1739302204-vyj7NGw24MskBY6gaQTTSdQeDpiFQUCy-0-34da501f0dd8c7d34c13c94fb144467d)
在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=1739302204-lBh02NADdU6jfwG2Vcg8IqpEY5l7N2Sa-0-35e9e80e122ebb6a1f7b916b573a3939)
在bindApplication方法中最后调用sendMessage方法向H发送BIND_APPLICATION类型消息,H的handleMessage方法如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer129.jpg?sign=1739302204-CtgZypZVSz3wZ9yX17FJQDQYlUXJ3O3o-0-f723e4748a0853b463b5ad7143ef2a5f)
我们接着查看handleBindApplication方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer130.jpg?sign=1739302204-BxgcYEH3LEsxpENIsqHzB981IKSvu3Jo-0-edf53184cfbdf1d185d4c047a022e91a)
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=1739302204-TC2SEd58HIQysLcrYF9SckqnqN4TsHwz-0-fd5799218f64fccc733e713b57755611)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer132.jpg?sign=1739302204-qbz1PerCc4DcKHeVJEo4dCIxzl40ycpC-0-313539ce5ecd3611a6a9ff4807110222)
在注释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=1739302204-8hEVUGHFAFMDL8Sogf5X8hJRn4JbyPhg-0-caa970fe0b5352673acb17c022c8cd96)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer134.jpg?sign=1739302204-zjr0UJj9cb18ibd9JVwvBNqqJWgrbHwz-0-df9183f21e4185c723aa2aa27de68c43)
在注释1处通过反射来创建ContentProvider类型的localProvider对象,并在注释2处调用了它的attachInfo方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer135.jpg?sign=1739302204-HJ02qtODec7FGPtrwk6u1HYwtQJTWmQa-0-517a8bfd1156efd075735e4515160b37)
在attachInfo方法中调用了onCreate方法,它是一个抽象方法,这样Content Provider就启动完毕。当然这只是Content Provider启动过程的一个分支,即应用程序进程没有启动的情况,还有一个分支是应用程序进程已经启动的情况,这就需要读者自行阅读源码了。