1. 程式人生 > >ContentProvider啟動流程分析(一)

ContentProvider啟動流程分析(一)

## 0x01 扯東扯西的前言&概述

作為安卓設計的四大元件之一,是跨程序共享資料的一把利器,所謂跨程序共享資料,通俗理解就是,應用程式A可以訪問操作應用程式B共享出來的資料,這些共享出來的資料一般都有其對應的URI(統一資源識別符號),那麼就涉及到兩個過程:

  1. 提供資料內容的過程:
    • A應用(比如系統聯絡人,日曆)如果希望共享自己的資料內容(比如聯絡人列表,日曆資訊),就需要通過子類重寫ContentProvider六個方法來實現,ContentProvider的六個方法的共性特徵是,都接受一個URI引數,通過解析URI引數匹配相應的資料內容並將其返回;
    • 當一個跨程序訪問資料的請求(包含RUI引數)發出後,系統首先會校驗URI許可權(authority),許可權校驗通過後,把這個訪問請求傳遞到對應程序(具體來說是一個應用程式)的ContentProvider元件,ContetnProvider接收到URI引數後,會解析URI路徑(path),然後根據路徑解析結果,提供相應的資料內容並將其返回;
    • 此過程在A應用程式內部實現,當A應用程式封裝好了ContentProvider實現類,需要在Mainfest清單檔案中進行註冊,至此,A應用程式所在的程序已經提供了訪問自己所在程序資料的介面,B應用程式只需要根據資料內容的URI,發出訪問請求即可;
  2. 發出訪問資料請求的過程:
    • B應用程式如果希望跨程序訪問A應用程式共享出來的資料,需要呼叫Context#getContentResolver()#query()|update()|insert()|delete(),無非就是對資料內容進行增刪改查操作,涉及到一個類ContentResolver,具體呼叫的是ContentResolver的增刪改查方法;與SQLite資料庫的增刪改查不同,ContentResolver的增刪改查方法需要接受一個URI引數,這個URI引數就是希望訪問的資料內容的URI;
    • 此過程在B應用程式內部實現,通過在B程序訪問A程序的私有資料,完成跨程序共享資料的過程!

模擬一個跨程序請求資料的場景:A應用程式(AxxApp)在MainActivity中向B應用程式(BxxApp,也即系統自帶的聯絡人應用)的聯絡人資料發起跨程序的訪問請求。B應用程式中,SubContentProvider類繼承ContentProvider元件類,並重寫了ContentProvider的六個成員函式。

根據以上場景,當A應用程式發出訪問請求,請求攜帶系統聯絡人資料URI,系統聯絡人資料對應的URI值是 ContactsContract.CommonDataKinds.Phone.CONTENT_URI,當訪問請求發出後,系統會根據對應的URI,啟動B應用程式中ContentProvider元件SubContentProvider,並把聯絡人資料返回給A應用程式。那麼B程式的SubContentProvider元件是經過哪些呼叫,一步一步被啟動的呢?請看時序圖如下:

ContentProvider啟動時序圖

## 0x02ContentProvider啟動流程分析

再來結合原始碼分步梳理一遍詳細經過,對應時序圖的step1-->step5,過程如下:

時序圖step1 --> Context#getContentResolver()

在A程式程序的MainActivity中呼叫getContentResolver函式,根據MainActivity的多重繼承關係,MainActivity繼承了Activity,而Activity又繼承了ContextWrapper,所以我們可以發現,因為當前Activity持有Context的引用,所以實際上呼叫的是ContextWrapper.getContentProvider()函式來獲得ContentResolver物件,記作resolver變數。

ContextWrapper類的成員函式getContentResolver()原始碼如下:

public class ContextWrapper extends Context {
    Context mBase;
    ....
    @Override
    public ContentResolver getContentResolver() {
        return mBase.getContentResolver();
    }
    ....
}

可以看到ContextWrapper類的成員變數mBase指向了一個ContextImpl物件,它是在MainActivity元件啟動時建立的,因此mBase.getContentResolver()實際上的呼叫是ContextImpl.getContentResolver()函式來獲得一個ContentResolver物件resolver的。

ContextImpl類的成員函式getContentResolver()原始碼如下:

class ReceiverRestrictedContext extends ContextWrapper {
    ....
    private final ApplicationContentResolver mContentResolver;
    ....
    /*建構函式*/
    private ContextImpl(ContextImpl container, ActivityThread mainThread,
            LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
            Display display, Configuration overrideConfiguration, int createDisplayWithId) {
        ....
        /*建立ApplicationContentResolver物件*/
        mContentResolver = new ApplicationContentResolver(this, mainThread, user);
    }
    
    /*返回ContentResolver物件*/
    @Override
    public ContentResolver getContentResolver() {
        return mContentResolver;
    }
}
  • ContextImpl類的成員變數mContentResolver,是一個ApplicationContentResolver物件,在構造方法中被建立後,直接在getContentResolver()函式中簡單的被返回給呼叫者。
  • 也即,MainActivity中getContentResolver()函式,最終獲得的是一個ApplicationContentResolver物件;
  • 接著就呼叫這個ApplicationContentResolver物件的acquireProvider()函式獲取BxxApp應用程式的SubContentProvider元件的一個代理物件;
  • 也就是獲得與ContactsContract.CommonDataKinds.Phone.CONTENT_URI聯絡人資料URI對應的一個ContentProvider元件物件。

時序圖step2,3 --> ContentResolver#acquireProvider()

ApplicationContentResolver是ContentResolver的實現類,它重寫了父類的acquireProvider()函式,所以實際上呼叫的是ContentResolver子類ApplicationContentResolver的成員函式acquireProvider(),ApplicationContentResolver#acquireProvider()原始碼如下:

private static final class ApplicationContentResolver extends ContentResolver {
    private final ActivityThread mMainThread;
    ....

    public ApplicationContentResolver(
            Context context, ActivityThread mainThread, UserHandle user) {
        super(context);
        mMainThread = Preconditions.checkNotNull(mainThread);
        ....
    }

    @Override
    protected IContentProvider acquireProvider(Context context, String auth) {
        return mMainThread.acquireProvider(context,
                ContentProvider.getAuthorityWithoutUserId(auth),
                resolveUserIdFromAuthority(auth), true);
    }
}
  • 可以看出ApplicationContentResolver類的成員變數mMainThread指向了一個ActivityThread物件,它是在建構函式中初始化的。
  • 在函式acquireProvider內部,其實呼叫的是ActivityThread類的成員函式acquireProvider(),這個函式會返回一個ContentProvider元件的代理物件,而這個代理物件代理的,正是聯絡人資料URI對應的COntentProvider元件!

時序圖step4—5 --> ActivityThread#acquireProvider()/acquireExistingProvider()

接下來看ActivityThread類的acquireProvider()函式的原始碼如下:

public final class ActivityThread {
    ....
    public final IContentProvider acquireProvider(
            Context c, String auth, int userId, boolean stable) {
        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
        if (provider != null) {
            return provider;
        }
        ....
        IActivityManager.ContentProviderHolder holder = null;
        try {
            holder = ActivityManagerNative.getDefault().getContentProvider(
                    getApplicationThread(), auth, userId, stable);
        } ....
        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }
    ....
    public final IContentProvider acquireExistingProvider(
        Context c, String auth, int userId, boolean stable) {
        synchronized (mProviderMap) {
            final ProviderKey key = new ProviderKey(auth, userId);
            final ProviderClientRecord pr = mProviderMap.get(key);
            if (pr == null) {
                return null;
            }

            IContentProvider provider = pr.mProvider;
            IBinder jBinder = provider.asBinder();
            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
            if (prc != null) {
                incProviderRefLocked(prc, stable);
            }
            return provider;
        }
    }
}
  • ActivityThread類的acquireProvider()函式中,首先通過acquireExistingProvider()函式得到一個ContentProvider元件的代理物件IContentProvider物件provider;
  • acquireExistingProvider()函式持有一個全域性的mProviderMap變數,表示一個HashMap
  • 此函式的邏輯是,從mProviderMap表中獲取與許可權引數(auth)以及使用者ID引數(userId)對應的ProviderClientRecord代理物件,並將其返回!

  • 再回到acquireProvider()函式中,得到provider物件後進行判空,如果provider非空則直接將其返回給呼叫者了;
  • 如果provider為空,接下來會先獲得ActivityManagerService類的一個代理物件,接著呼叫ActivityManagerService代理物件的成員函式getContentProvider()來請求與許可權引數(auth)以及使用者ID引數(userId)對應的ContentProvider元件的代理物件;
  • 並通過ActivityManagerService將這個代理物件返回。ActivityManagerService返回的ContentProvider元件的代理物件,使用一個ContentProviderHolder物件來進行描述。

  • 接下來,會呼叫ActivityThread類的成員函式installProvider(),將這個ContentProviderHolder物件封裝成一個ContentClientRecord物件,並把這個ContentClientRecord物件存入全域性變數mProviderMap中。
  • 這樣如果後面再次接收到,對相同URI對應的ContentProvider的訪問請求,對於每個ContentProvider元件來說了,只需要被ActivityThread請求一次即可,當再次收到相同的請求只需要從全域性變數mProviderMap中將其取出來返回給呼叫者即可!

然後按照函式執行的先後順序,分為兩個片段,先分析ActivityManagerService代理物件(記作ActivityManagerProxy)的成員函式getContentProvider()的具體實現(對應時序圖step6—19),然後再分析ActivityThread類的成員函式installProvider()的具體實現(對應時序圖step20)。

## 0x03 參考文獻與簡單的結語

未完,轉接下一篇:ContentProvider啟動流程分析二(對應時序圖step6—14)!

相關推薦

ContentProvider啟動流程分析

## 0x01 扯東扯西的前言&概述 作為安卓設計的四大元件之一,是跨程序共享資料的一把利器,所謂跨程序共享資料,通俗理解就是,應用程式A可以訪問操作應用程式B共享出來的資料,這些共享出來的資料一般都有其對應的URI(統一資源識別符號),那麼就涉及到兩個過程: 提供資料內容的過程: A應用(

Android9.0 Activity啟動流程分析

1、ActivityRecord、TaskRecord、ActivityStack和ActivityDisplay介紹   本篇文章是基於Android refs/tags/android-9.0.0_r8分支的程式碼進行分析的   在分析Activity啟動的原始碼之前先介紹一下Act

ContentProvider啟動流程分析

## 0x01 扯東扯西的前言&概述 ## 0x02 ContentProvider啟動流程分析 step15: ApplicationThread#bindApplication() 上一步,在ApplicationThreadProxy類的bindApplication()函式中,通過B

ContentProvider啟動流程分析

## 0x01 扯東扯西的前言&概述 ## 0x02 ContentProvider啟動流程分析 step6: ActivityManagerProxy#getContentProvider() 代理類ActivityManagerProxy位於ActivityManagerNative.j

springboot啟動流程分析

以springboot 2.0.2.RELEASE版本為例 1.pom.xml引入依賴 <parent> <groupId>org.springframework.boot</groupId>

SpringBoot啟動流程分析:SpringApplication類初始化過程

SpringBoot系列文章簡介 SpringBoot原始碼閱讀輔助篇:   Spring IoC容器與應用上下文的設計與實現 SpringBoot啟動流程原始碼分析: SpringBoot啟動流程分析(一):SpringApplication類初始化過程 SpringBoot啟動流程分析(二)

SpringMVC的流程分析—— 整體流程概括

classes amp 不同 方法 restfu equals 類圖 strong .get SpringMVC的整體概括 之前也寫過springmvc的流程分析,只是當時理解的還不透徹所以那篇文章就放棄了,現在比之前好了些,想著寫下來分享下,也能增強記憶,也希望可以幫助到

Android9.0 Activity啟動流程分析

文章目錄 1、ActivityThread的main函式 2. AMS的attachApplication函式 2.1 Part-I 2.2 Part-II 2.2.1 ApplicationThread的bindApp

Web前端-Vue專案執行流程分析

去年年底離職了創業去了哈,把寫部落格的事情給落下了! 一直盯著專案幾乎很少休息,一個月能休息一天算好的啦,煎熬將近一年啊。 都說 “離職窮半年,創業窮三年”,終於嚐到其中滋味!哎。。。現在終於涼了!哈哈! 最近靜下心來,好好反思總結,其中寫部落格這件事,還是不能停止..

Vue框架專案實戰整理:3、Vue專案執行流程分析

宣告:本教程不收取任何費用,歡迎轉載,尊重作者勞動成果,不得用於商業用途,侵權必究!!!! 上兩篇講了Vue框架專案實戰整理:1、快速啟動Vue、開發工具介紹 和 Vue框架專案實戰整理:2、Vue環境搭建,今天應該到Vue專案執行流程分析的時候了,為什麼會有這方便的研究

dart 非同步事件執行流程分析

深入研究了dart 的非同步事件執行流程。   main() { /// testNoAwait() and testWithCallerAwait() exe order is same. // testNoAwait(); // testWithCal

[Android6.0][RK3399] 雙屏異顯程式碼實現流程分析

Platform: RK3399 OS: Android 6.0 Version: v2016.08 本文分為兩部分。 《[RK3399] 雙屏異顯程式碼實現流程分析(一)》為分析 RK video 部分標準的程式碼(base on 2017.

Android OTA升級原理和流程分析

這篇及以後的篇幅將通過分析update.zip包在具體Android系統升級的過程,來理解Android系統中Recovery模式服務的工作原理。我們先從update.zip包的製作開始,然後是Android系統的啟動模式分析,Recovery工作原理,如何從

springboot啟動流程分析

現在繼續看啟動過程的詳情,詳細描述下SpringApplication建構函式: 1.載入過程中的SpringApplication初始化如下: public SpringApplication(ResourceLoader resourceLoader

Activity啟動流程筆記

從startActivity開始說起: public void startActivity(Intent intent, Bundle options) { if (options != null) { startA

STM32 eCos 啟動程式碼分析系統復位

概述 最近接觸了STM32,開始瞭解CortexM3系列ARM處理器上RTOS的移植和啟動。 開始總是艱難的,CortexM3是arm7tdmi的升級產品,但實際上和之前的ARM7有著很大的區別。 首先,我們必須有支援CortexM3的編譯器,因為CortexM3採用的是T

Vsync垂直同步訊號分發和SurfaceFlinger響應執行渲染流程分析

void SurfaceFlinger::init() { ALOGI( "SurfaceFlinger's main thread ready to run. " "Initializing graphics H/W..."); status_t err;

electron-vue架構解析3-開發環境啟動流程分析

上一節我們介紹了生產環境的打包流程,這一節我們來看開發環境的啟動流程。 該框架主要修改是對開發環境的優化,包括了於開發環境的配置檔案隔離,主程序和渲染程序配置檔案隔離,編譯過程提示等功能,因此這一節內容才是整個框架的核心。 我們從開發人員用到的啟動命令

SpringBoot啟動流程分析:SpringApplication的run方法

SpringBoot系列文章簡介 SpringBoot原始碼閱讀輔助篇:   Spring IoC容器與應用上下文的設計與實現 SpringBoot啟動流程原始碼分析: SpringBoot啟動流程分析(一):SpringApplication類初始化過程 SpringBoot啟動流程分析(二)

SpringBoot啟動流程分析:IoC容器的初始化過程

SpringBoot系列文章簡介 SpringBoot原始碼閱讀輔助篇:   Spring IoC容器與應用上下文的設計與實現 SpringBoot啟動流程原始碼分析: SpringBoot啟動流程分析(一):SpringApplication類初始化過程 SpringBoot啟動流程分析(二)