![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=1738886155-NZAFnV721gucJuVAYmvOSegfTywgAKb4-0-829b097eb387b29605ddf59f4e8dcf30)
在ContentProviderActivity中,我们在GameProvider中插入了一条数据,可见要想插入一条数据,或者说使用ContentProvider,需要先调用getContentResolver方法,如下所示:这里的mBase已经是我们的老朋友了,指的是ContextImpl,ContextImpl的getContentResolver方法如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer112.jpg?sign=1738886155-OYb7prAlSogBBwDmY27qHkYrnfdMkJuE-0-a769edec9a72b72ad24840dee08f29ee)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer113.jpg?sign=1738886155-pg8W8FfTS9CGk6MLEscElGwWtY5AO0t2-0-14fbce71c1afd89aab7529e375952edf)
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=1738886155-8zKG1MD1DqDsqvgonrUrpBXlGKE0gaeg-0-8a6588398ba7548e29f1453ed2b90f73)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer115.jpg?sign=1738886155-TeMTqMY1AOz82SAryowX82s0tPDksW1q-0-a7fc3b2f203ac9acf1d591be704c7081)
在注释1处通过acquireUnstableProvider方法返回IContentProvider类型的unstableProvider对象,在注释2处调用unstableProvider的query方法。IContentProvider是ContentProvider 在本地的代理,具体的实现为ContentProvider,我们查看ContentProvider的acquireUnstableProvider方法做了什么,如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer116.jpg?sign=1738886155-Hke5kMzkAtAwMtru2m3vDVOWjrb47XMq-0-91bd669cdd01d82baf524a6a31eb5b9d)
在注释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=1738886155-aHmmRrL8kKXtA0dKnrMROj7cjXkeJsRQ-0-714db0862763207e51bb465ae1bb3da5)
在acquireUnstableProvider方法中返回了ActivityThread类型的mMainThread对象的acquireProvider方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer118.jpg?sign=1738886155-WPqe9vOvf4G1FMBPhI2O5IHJ6PeUoOCB-0-79df08b073d66330a2c616bb69ea6dd9)
注释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=1738886155-5p7yYzc6mReIo7BW0p3CStbvmHJM2ePP-0-2594edf93cbe29379ce1de4d105141bb)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer120.jpg?sign=1738886155-nkpd2sXfVxT62fHPe029gdVg3mGeJD61-0-326df6ebb59e7d3f6b050ed3f807fb72)
getContentProvider方法返回了getContentProviderImpl方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer121.jpg?sign=1738886155-3HUvV8o2SqATjx5CbsAgItheVwSAeZl4-0-d9cee83b270998a9ad5f8b83f0459290)
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=1738886155-75wZUTNDCnYLvtT9f8jwixYwyV4qFQUm-0-9dfe3f378d16353fe6f4d69dd831810f)
在注释1处通过prepareMainLooper方法在ThreadLocal中获取Looper,并在注释3处开启消息循环。在注释2处创建了ActivityThread,紧接着调用了它的attach方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer123.jpg?sign=1738886155-uM50tHtzfPZBOUd4l22pYq9ZpJRcd43X-0-01ad1003280a3b6c2ae5d55f56b91b3c)
注释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=1738886155-FP7JeH8GjX3GrOjDcmmFICHSvSULw1qz-0-8987abed3ce43c789d331ddf005eb004)
图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=1738886155-e0tNiGwvNWeio2gjsXo95IIz0KV33fL7-0-acafa475291259b889a10c6353c3120b)
图4-16 AMS启动Content Provider的过程的时序图
我们接着来查看AMS的attachApplication方法,如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer125.jpg?sign=1738886155-SxGhp1h5Rizy1DTpzsrBxmi06arjs27s-0-6a4074dbca0a3e6c2df7aef68d7e1919)
在attachApplication方法中又调用了attachApplicationLocked方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer127.jpg?sign=1738886155-y3wpdCbqzFVCQxchXuetuBkPLrYdqSeT-0-d320e558d7cd873083e04fe80aac3aa1)
在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=1738886155-lQ7HBGiV9lM8HvPzFgiaDGKtBkAJWrFV-0-e0f3dec2c5898719c1a0424d74499445)
在bindApplication方法中最后调用sendMessage方法向H发送BIND_APPLICATION类型消息,H的handleMessage方法如下所示:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer129.jpg?sign=1738886155-4AHmqP0A081ytMhIIJYATQmzUKhKrBXW-0-60d6837da4daf64bf7c5242f437eb947)
我们接着查看handleBindApplication方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer130.jpg?sign=1738886155-95ReRySx626bc2SrgbitIY1ndYnJoAKH-0-611bbe9183c38651c77014aa4f6bbd03)
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=1738886155-nDXPPfFDYBarOK5jLuKXsdYsI6iutWNe-0-6442939c351542b162001deb289d5c05)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer132.jpg?sign=1738886155-Kd7YpL1e2mxMx4CRiI8svu1Pl6SJiFRM-0-d87cced8288e53db4623f326856e7c86)
在注释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=1738886155-g9mtS9Kp1tI7HyUvn1ux3EY169nr4MXK-0-38a2d39eccbdbbb9161842b124e31a13)
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer134.jpg?sign=1738886155-XpIqLTY7BaFf0xw5XVi8HaxseRz8IF5y-0-8811a543102b0bf29689e6ab1d9d3c45)
在注释1处通过反射来创建ContentProvider类型的localProvider对象,并在注释2处调用了它的attachInfo方法:
![](https://epubservercos.yuewen.com/D63A94/16896237205618706/epubprivate/OEBPS/Images/figer135.jpg?sign=1738886155-SoNLvsC5BRhz3gGO8BHegVvCaOSEwsxJ-0-2767626bfda7c787a574990e2db355cd)
在attachInfo方法中调用了onCreate方法,它是一个抽象方法,这样Content Provider就启动完毕。当然这只是Content Provider启动过程的一个分支,即应用程序进程没有启动的情况,还有一个分支是应用程序进程已经启动的情况,这就需要读者自行阅读源码了。