Jetpack 最新成員 AndroidX App Startup 實踐以及原理分析

前言

前幾天 Google 更新了幾個 Jetpack 新成員 Hilt、Paging 三、App Startup 等等,週末空閒時間實踐了一下 App Startup 能夠前去查看 GitHub 上的項目 AndroidX-Jetpack-Practice ,接下來一塊兒來分析一下 AndroidX App Startup。java

經過這篇文章你將學習到如下內容:node

  • App Startup 是什麼?
  • App Startup 爲咱們解決了什麼問題?
  • 爲何不管是 Google 仍是第三方庫,初始化時都會在 ContentProvider 裏面進行初始化?
  • 在 ContentProvider 裏初始化會帶來什麼性能問題?
  • ContentProvider 啓動順序源碼分析?
  • 如何正確使用 App Startup?
    • 自動初始化。
    • 手動初始化(也是延遲初始化)。

App Startup 是什麼?

來自 Google 文檔: App Startup 是 Android Jetpack 最新成員,提供了在 App 啓動時初始化組件簡單、高效的方法,不管是 library 開發人員仍是 App 開發人員均可以使用 App Startup 顯示的設置初始化順序。android

簡單的說就是 App Startup 提供了一個 ContentProvider 來運行全部依賴項的初始化,避免每一個第三方庫單獨使用 ContentProvider 進行初始化,從而提升了應用的程序的啓動速度。git

不管是 Google 提供的庫仍是第三方庫,啓動時運行一些初始化邏輯並很多見,例如 WorkManager 在應用啓動時使用 ContentProvider 進行初始化,來看一下 Google 工程師 Husayn Hakeem 分享的一張的圖。github

LibraryA, LibraryB, and LibraryC initialized using their own ContentProviders

上圖表示如今咱們有三個庫分別 LibraryA、LibraryB、和 LibraryC 它們使用本身的 ContentProviders 進行初始化。面試

而 App Startup 提供了一個 ContentProvider 來運行全部依賴項的初始化(LibraryA、LibraryB、和 LibraryC),以下圖所示。算法

LibraryA, LibraryB, and LibraryC initialized by AndroidX Startup

AndroidX App Startup 爲咱們解決了什麼問題?

剛纔咱們說到不管是 Google 提供的庫仍是第三方庫,App 啓動運行時會初始化一些邏輯,它們爲了方便開發者使用,避免開發者手動調用,使用 ContentProvider 進行初始化,例如 WorkManager 在應用啓動時使用 ContentProvider 進行初始化,咱們來看一下 WorkManager 的源碼,先來看一下 AndroidManifest.xml 文件內容。編程

如上所見,咱們能夠看到在 AndroidManifest.xml 文件內定義了一個名爲 WorkManagerInitializer 的 ContentProvider,我來看看 WorkManagerInitializer 裏面都作了什麼。數組

public class WorkManagerInitializer extends ContentProvider {
    @Override
    public boolean onCreate() {
        // Initialize WorkManager with the default configuration.
        WorkManager.initialize(getContext(), new Configuration.Builder().build());
        return true;
    }
    
    ......
    // 省略了沒用的代碼
}
複製代碼

如上所見其實就是在 WorkManagerInitializer 的 onCreate() 方法裏面,使用默認配置初始化 WorkManager。性能優化

咱們也來模仿 WorkManager 寫一個 Demo,這裏只貼出部分代碼,更多信息查看 GitHub 上的 AppStartupSimple 下面的 ContentProvider 模塊。

  • 定義一個 WorkContentProvider 並在 onCreate 方法中打印一行日誌。
class WorkContentProvider : ContentProvider() {

    override fun onCreate(): Boolean {
        Log.d(TAG, "WorkContentProvider create()")
        return true
    }
    
    .....
}
複製代碼
  • 在 AndroidManifest.xml 文件中註冊 WorkContentProvider。
<application>
    <provider
        android:name=".WorkContentProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false" />
</application>
複製代碼
  • 運行 App 日誌以下所示。
com.hi.dhl.startup.simple D/WorkContentProvider: WorkContentProvider create()
複製代碼

假設你的 App 有不少相似於 WorkManager 這樣的庫,都在 ContentProvider 裏面進行一些初始化工做,在 App 啓動時運行多個 ContentProvider,這樣會帶來一些問題:

  • 多個 ContentProvider 會增長了 App 啓動運行的時間。
  • ContentProvider 的 onCreate 方法會先於 Application 的 OnCreate 方法執行,這是在冷啓動階段自動運行初始化的,來看一下 Android 10 系統源碼。
private void handleBindApplication(AppBindData data) {
	......

	if (!data.restrictedBackupMode) {
        if (!ArrayUtils.isEmpty(data.providers)) {
        	// 建立ContentProvider
            installContentProviders(app, data.providers);
        }
    }

	......

    try {
            // 調用調用 Application 的 OnCreate 方法
            mInstrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
            ......
        }

    ......
 }
複製代碼

這是在 App 冷啓動時自動運行初始化的,這樣只會增長 App 的加載時間,用戶但願 App 加載得快,啓動慢會帶來糟糕的用戶體驗,AndroidX App Startup 正是爲了解決這個問題而出現的

如何正確使用 AndroidX App Startup?

使用 AndroidX App Startup 來運行全部依賴項的初始化有兩種方式:

  • 自動初始化。
  • 手動初始化(也是延遲初始化)。

具體能夠查看 GitHub 上的 AppStartupSimple 下面的 Startup-Library 模塊相關代碼。

自動初始化

  • 在 build.gradle 文件內添加依賴。
implementation "androidx.startup:startup-runtime:1.0.0-alpha01"
複製代碼
  • 實現 Initializer 接口,並重寫兩個方法,來初始化組件。
class LibaryC : Initializer<LibaryC.Dependency> {
    override fun create(context: Context): Dependency {
        // 初始化工做
        Log.e(TAG, "init LibaryC ")
        return Dependency()
    }

    override fun dependencies(): MutableList<Class<out Initializer<*>>> {
        return mutableListOf(LibaryB::class.java)
    }
    ......
}
複製代碼
  • create(Context): 這裏進行組件初始化工做。
  • dependencies(): 返回須要初始化的列表,同時設置 App 啓動時依賴庫運行的順序,假設 LibaryC 依賴於 LibaryB,LibaryB 依賴於 LibaryA,App 啓動運行時,會先運行 LibaryA 而後運行 LibaryB 最後運行 LibaryC。

正如 GitHub 上的 AppStartupSimple 示例項目,它依賴結構就是 LibaryC 依賴於 LibaryB,LibaryB 依賴於 LibaryA,輸出結果以下所示:

com.hi.dhl.startup.simple E/LibaryA: init LibaryA 
com.hi.dhl.startup.simple E/LibaryB: init LibaryB 
com.hi.dhl.startup.simple E/LibaryC: init LibaryC 
複製代碼
  • 在 AndroidManifest.xml 文件中註冊 InitializationProvider。
<application>
    <provider
        android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false"
        tools:node="merge">

        <!-- 自動初始化 -->
        <meta-data
            android:name="com.hi.dhl.startup.library.LibaryC"
            android:value="androidx.startup" />
    </provider>
</application>
複製代碼

App 啓動的時 App Startup 會讀取 AndroidManifest.xml 文件裏面的 InitializationProvider 下面的 <meta-data> 聲明要初始化的組件,完成自動初始化工做。

手動初始化(也是延遲初始化)

  • 在 build.gradle 文件內添加依賴,和上文同樣。
  • 建立一個類 LibaryD 實現 Initializer 接口,並重寫兩個方法,來初始化組件,和上文同樣。
  • 在 AndroidManifest.xml 文件中註冊 InitializationProvider
<application>
        <provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false"
            tools:node="merge">
            <!-- 手動初始化(也是延遲初始化) -->
            <meta-data
                android:name="com.hi.dhl.startup.library.LibaryD"
                android:value="androidx.startup"
                tools:node="remove" />
        </provider>
    </application>
複製代碼

只須要在 <meta-data> 標籤內添加 tools:node="remove" 清單合併工具會將它從清單文件中刪除。

  • 在須要的地方進行初始化,調用如下代碼進行初始化。
AppInitializer.getInstance(context).initializeComponent(MyInitializer::class.java)
複製代碼

若是組件初始化以後,再次調用 AppInitializer.initializeComponent() 方法不會再次初始化。

手動初始化(也是延遲初始化)是很是有用的,組件不須要在 App 啓動時運行,只須要在須要它地方運行,能夠減小 App 的啓動時間,提升啓動速度。

總結

這篇文章主要介紹瞭如下內容:

  • ContentProvider 啓動順序源碼分析。
  • App Startup 是 Jetpack 的新成員,是爲了解決因 App 啓動時運行多個 ContentProvider 會增長 App 的啓動時間的問題。
  • 使用了一個 InitializationProvider 管理多個依賴項,消除了每一個庫單獨使用 ContentProvider 成本,減小初始化時間。
  • App Startup 容許你自定義組件初始化順序。
  • App Startup 能夠自動初始化 AndroidManifest.xml 文件中 InitializationProvider 下面的 <meta-data> 聲明要初始化的組件。
  • App Startup 提供了一種延遲初始化組件的方法,減小 App 初始化時間。

計劃創建一個最全、最新的 AndroidX Jetpack 相關組件的實戰項目 以及 相關組件原理分析文章,正在逐漸增長 Jetpack 新成員,倉庫持續更新,能夠前去查看:AndroidX-Jetpack-Practice, 若是這個倉庫對你有幫助,請幫我點個贊,我會陸續完成更多 Jetpack 新成員的項目實踐。

參考文獻

結語

致力於分享一系列 Android 系統源碼、逆向分析、算法、翻譯、Jetpack 源碼相關的文章,能夠關注我,若是你喜歡這篇文章歡迎 star,一塊兒來學習,期待與你一塊兒成長。

算法

因爲 LeetCode 的題庫龐大,每一個分類都能篩選出數百道題,因爲每一個人的精力有限,不可能刷完全部題目,所以我按照經典類型題目去分類、和題目的難易程度去排序。

  • 數據結構: 數組、棧、隊列、字符串、鏈表、樹……
  • 算法: 查找算法、搜索算法、位運算、排序、數學、……

每道題目都會用 Java 和 kotlin 去實現,而且每道題目都有解題思路,若是你同我同樣喜歡算法、LeetCode,能夠關注我 GitHub 上的 LeetCode 題解:Leetcode-Solutions-with-Java-And-Kotlin,一塊兒來學習,期待與你一塊兒成長。

Android 10 源碼系列

正在寫一系列的 Android 10 源碼分析的文章,瞭解系統源碼,不只有助於分析問題,在面試過程當中,對咱們也是很是有幫助的,若是你同我同樣喜歡研究 Android 源碼,能夠關注我 GitHub 上的 Android10-Source-Analysis,文章都會同步到這個倉庫。

Android 應用系列

精選譯文

目前正在整理和翻譯一系列精選國外的技術文章,不只僅是翻譯,不少優秀的英文技術文章提供了很好思路和方法,每篇文章都會有譯者思考部分,對原文的更加深刻的解讀,能夠關注我 GitHub 上的 Technical-Article-Translation,文章都會同步到這個倉庫。

工具系列

逆向系列

相關文章
相關標籤/搜索