扣丁書屋

聊聊Android資源加載那些事,Resource的初始化

前言

在之前的文章中,我們通過探討 Resource.getx() 等方法,從而解釋了相關方法的背后實現。那么,不知道你有沒有好奇 context.resources 與 Resource.getSystem() 有什么不同呢?前者又是在什么時候被初始化的呢?

如果你對上述問題依然存疑,或者你想在復雜中找到一個較清晰的脈絡,那本文可能會對你有所幫助。本篇將與你一同探討關于 Resources 初始化的那些事。

本篇定位中等,主要通過偽源碼的方式探索 Resources 的初始化過程。

targetSdk:31

導航

學完本篇,你將明白以下內容:

  • Resource(Activity)、Resource(App) 初始化流程
  • Context.resource 與 Resource.getSystem() 的不同之處

基礎概念

開始本篇前,我們先了解一些必備的基礎概念:

ActivityResource

用于持有 Activity 或者 WindowContext 相關聯的 Resources。

ActivityResources

用于管理 Acitivty 的 config 和其所有 ActivityResource,以及當前正在顯示的屏幕id。

ResourceManager

用于管理 App 所有的 resources,內部有一個 mActivityResourceReferences map保存著所有 activity 或者 windowsToken 對應的 Resources 對象。

Resource(Activity)

在 Activity 中調用 getX() 相關方法時,點進源碼不難發現,內部都是調用的 getResource().x ,而 getResource() 又是來自 Context,所以一切的源頭也即從這里開始。

了解 context 的小伙伴應該有印象, context 作為一個頂級抽象類,無論是 Activity 還是 Application 都是其的子類, Context 的實現類又是 ContextImpl,所以當我們要找 Activity 中 resource 在哪里被初始化時,也即是在找:

-> ContextImpl.resource 在哪里被初始化? ??

順藤摸瓜,我們去看看 ContextImpl.createActivityContext()。

該方法的調用時機是在構建我們 Activity 之前調用,目的是用于創建 context 實例。

流程分析

具體如下:

ContextImpl.createActivityContext ->

上述總結如下:

內部會獲取當前的分辨率 、classLoader 等配置信息,并調用 ResourcesManager.getInstance() 從而獲取 ResourcesManager 的單例對象,然后使用其的 createBaseTokenResources() 去創建最終的 Resources 。**接著將resource對象保存到 context 中,即賦值給 ContextImpl.mResources 。**ps: 如果 sdk>=26 ,還會做 CompatResources 的判斷。

了解了上述流程,我們接著去看 resourcesManager.createBaseTokenResources() 。 ResourceManager.createBaseTokenResources()

上述總結如下:

該方法用于創建當前 activity 相對應的 resources,內部會經歷如下步驟:

  1. 先查找或創建當前 token(activity) 所對應的 resources;

a . Yes -> 什么都不做

b . No -> 創建一個 ActivityResources,并將其添加到 mActivityResourceReferences map中

2 . 接著再去更新該 activity 對應 resources(內部會再次執行第一步)

3 . 再次查找當前的 resources,如果找到,則直接返回;

4 . 如果找不到,則重新創建一個 resources(內部又會再執行第一步)

具體的步驟如下所示:

getOrCreateActivityResourcesStructLocked() ResourcesManager.getOrCreateActivityResourcesStructLocked()。

private ActivityResources getOrCreateActivityResourcesStructLocked(
        IBinder activityToken) {
      // 先從map獲取
    ActivityResources activityResources = mActivityResourceReferences.get(activityToken);
      // 不存在,則創建新的,并以token為key保存到map中,并返回新創建的ActivityResources
    if (activityResources == null) {
        activityResources = new ActivityResources();
        mActivityResourceReferences.put(activityToken, activityResources);
    }
    return activityResources;
}

如題所示,獲取或創建 ActivityResources 。如果存在則返回,否則創建并保存到 ResourcesManager.mActivityResourceReferences中。

updateResourcesForActivity()

ResourcesManager.updateResourcesForActivity()。

流程如下:

內部會先獲取當前 activity 對應的 resources(如果不存在,則創建),如果當前傳入的配置與之前一致,則直接返回。

否則先使用當前 activity 對應的配置 創建一個 [舊]配置對象,接著去更新該 activity 所有的 resources 具體實現類impl。每次更新時會先與先前的配置進行差異更新并返回新的 ReourcesKey ,并使用這個 key 獲取其對應的 impl(如果沒有則創建),獲取到的 resource 實現類 impl 如果與當前的不一致,則更新當前 resources 的 impl。

findResourcesForActivityLocked()

ResourcesManager.findResourcesForActivityLocked()。

流程如下:

當通過 findResourcesForActivityLocked() 獲取指定的 resources 時,內部會先獲取當前 token 對應的 activityResources ,從而拿到其所有的 resources。然后遍歷所有 resources,如果某個 resouces 對應的 key(ResourcesKey)與當前查找的一致并且符合其他規則,則直接返回,否則無符合條件時返回null。

createResourcesForActivity()

ResourcesManager.createResourcesForActivity()。

流程如下:

在創建 Resources 時,內部會先使用 key 查找相應的 ResourcesImpl,如果沒找到,則直接返回null,否則調用 createResourcesForActivityLocked() 創建新的 Resources。

總結

當我們在 Activity、Fragment 中調用 getX() 相關方法時,由于 context 只是一個代理,提供了獲取 Resources 的 getx() 方法,具體實現在 ContextImpl。所以在我們的 Activity 被創建之前,會先創建 contextImpl,從而調用 createActivityContext() 這個方法內部完成了對 resources 的初始化。內部會先拿到 ResourcesManager(用于管理我們所有resources),從而調用其的 createBaseTokenResources() 去創建所需要的 resources,然后將其賦值給 contextImpl。

在具體的創建過程中分為如下幾步:

  1. 先從 ResourcesManager 緩存 (mActivityResourceReferences) 中去找當前 token(Ibinder) 所對應的 ActivityResources,如果沒找到則重新創建一個,并將其添加到 map 中;
  2. 接著再去更新當前 token 所關聯 ActivityResources 內部(activityResource)所有的 resources,如果現有的配置參數與當前要更新的一致,則跳過更新,否則遍歷更新所有 resources;
  3. 再去獲取所需要的 resources,如果找到則返回,否則開始創建新的 resources;
  4. 內部會先去獲取 ResourcesImpl,如果不存在則會創建一個新的,然后帶上所有配置以及 token 去創建相應的 resources,內部也同樣會執行一遍第一步,然后再創建 ActivityResource,并將其添加到第一步創建的 activityResouces 中。

5 . Resrouces(Application)

Application 級別的,我們應該從哪里找入口呢?

既然是 Application 級別,那就找找 Application 什么時候初始化?而 Resources 來自 Context,所以我們要尋找的位置又自然是 ContextImpl 了。故此,我們去看看。

-> ContexntImpl.createSystemContext()

該方法用于創建 App 的第一個上下文對象,即也就是 AppContext。

流程分析

ContexntImpl.createSystemContext()

fun createSystemContext(mainThread:ActivityThread) {
      // 創建和系統包有關的資源信息
    val packageInfo = LoadedApk(mainThread)
    ...
    val context = ContextImpl(xxx)
      ??
    context.setResources(packageInfo.getResources())
    ...
    return context
}

如上所示,當創建系統 Context 時,會先初始化一個 LoadedApk ,用于管理我們系統包相關信息,然后再創建 ContextImpl ,然后調用創建好的 LoadedApk 的 getResources() 方法獲取系統資源對象,并將其設置給我們的 ContextImpl 。

?? LoadedApk.getResources()

當我們獲取 resources 時,內部會先判斷是否存在,如果不存在,則調用 ResourcesManager.getResources() 去獲取新的 resources 并返回,否則直接返回現有的。相應的,我們再去看看 ResourcesManager.getResources() 。

???? ResourcesManager.getResources()

如上所示,內部會對傳入的 activityToken 進行判斷,如果為不為 null,則調用 createResourceForActivity() 去創建;否則調用 createResources() 去創建,具體內部的邏輯和最開始相似,內部會先使用 key 查找相應的 ResourcesImpl,如果沒找到,則分別調用相關方法再去創建 Resources 。

關于 createResourceLocked(),我們再看一眼,如下所示:

這個方法內部創建了一個新的 resources,最終將其 add 到了 ResourcesManager.mResourceReferences 這個List中,以便復用。

總結

當我們的 App 啟動后,初始化 Application 時,會調用到 ContexntImpl.createSystemContext(),該方法內部同時也會完成對我們Resources 的初始化。內部流程如下:

  1. 先初始化 LoadedApk 對象(其用于管理app的信息),再調用其的 getResources() 方法獲取具體的 Resources;
  2. 在上述方法內部,會先判斷當前 resources 是否為 null。如果為 null,則使用 ResourcesManager.getResources() 去獲取,因為這是 application 的初始化,所以不存在 activityToken,故內部會直接調用 ResourceManager.createResource() 方法,內部會創建一個新的 Resources 并將其添加到 mResourceReferences 緩存中。

Resources(System)

大家都應該見過這樣的代碼,比如 Resources.getSystem().getX(),而他內部的實現也非常簡單,如下所示:

Tips

當我們使用 Resources.getSystem() 時,其實也就是在調用當前 framework 層的資源對象,內部會先判斷是否為 null,然后進行初始化,初始化的過程中,因為系統框架層的資源,所以實際的資源管理器直接調用了 AssetManager.getSystem(),這個方法內部會使用當前系統框架層的apk作為資源路徑。所以我們自然也無法用它去加載我們 Apk 內部的資源文件。

小問題

在了解了上述流程后,如果你存在以下問題(就是這么倔強),那么不妨鼓勵鼓勵自己,[你沒掉隊]!

1.為什么要存在 ActivityResources 與 ActivityResource ? 我們一直調用的不都是Resources嗎?

首先說說 ActivityResource,見名知意,它是作為 Resources 的包裝類型出現,內部持有當前要加載的配置,以及真正的 Resources,以便配置變更時更新 resources。

又因為一個 Activity 可能關聯多個 Resources,所以 ActivityResources 是一個 activity(或者windowsContext)的所有 resources 合集,內部用一個List維護,而 ActivityResources 又被 ResourcesManager 緩存著。

當我們每次初始化Act時,內部都會創建相應的 ActResources,并將其添加到manager中作為緩存。最終的 resources 只是一個代理對象,從而供開發者調用,真正的實現者 ResourcesImpl 則被全局緩存。

2.Resources.getSystem() 獲取應用drawable,為什么會報錯?

原因也很簡單啊,因為 resources 相應的 AssetManager 對應的資源路徑時 frameWork 啊,你讓它獲取當前應用資源,它不造啊。

總結

最終,讓我們反推上去,總體再來回顧一下 Resources 初始化的相關?:

  • 原來我們的 resources 都是在 context 創建時初始化,而且我們所調用的 resources 實際上被 ActivityResource 所包裝;
  • 原來我們的 Resources 只是一個代理,最終的調用其實是 ResourcesImpl,并且被 ResourcesManager 所緩存。
  • 原來每當我們初始化一個 Activity,我們所有的 resources 都會被刷新,為什么呢,因為我們的 config 配置可能會改變,比如深色模式切換等。
  • 原來 Resource.getSystem() 無法加載應用資源的原因只是因為 AssetManager 對應的資源路徑是 frameWork.apk 。

本篇中,我們專注于一個概念,即 resources 到底從何而來,并且從原理上分析了不同 context resources 的初始化流程,也明白了他們之間的區別與差異。

細心的小伙伴會發現,從上一篇,我們從應用層 Resources.getx() 開始,到現在 Resources 初始化。我們沿著開發者的使用習慣由淺入深,去探索底層設計,逐漸理清 Android Resources 的整體脈絡。


https://mp.weixin.qq.com/s/eA1P0iaLOYFnKaX_WqOxgg

最多閱讀

簡化Android的UI開發 3年以前  |  515859次閱讀
Android 深色模式適配原理分析 2年以前  |  27474次閱讀
Android 樣式系統 | 主題背景覆蓋 2年以前  |  8727次閱讀
Android Studio 生成so文件 及調用 2年以前  |  6741次閱讀
30分鐘搭建一個android的私有Maven倉庫 4年以前  |  5508次閱讀
Android設計與開發工作流 3年以前  |  5225次閱讀
移動端常見崩潰指標 2年以前  |  5081次閱讀
Android陰影實現的幾種方案 5月以前  |  5058次閱讀
Google Enjarify:可代替dex2jar的dex反編譯 4年以前  |  5018次閱讀
Android內存異常機制(用戶空間)_NE 2年以前  |  4766次閱讀
Android-模塊化-面向接口編程 2年以前  |  4667次閱讀
Android多渠道打包工具:apptools 4年以前  |  4570次閱讀
Google Java編程風格規范(中文版) 4年以前  |  4423次閱讀
Android死鎖初探 2年以前  |  4391次閱讀

手機掃碼閱讀
18禁止午夜福利体验区,人与动人物xxxx毛片人与狍,色男人窝网站聚色窝
<蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>