應用啓動性能 | 介紹 App Startup 庫

最近我開始嘗試使用 AndroidX 的應用啓動 (App Startup) 庫。在這個庫發 布了 1.0 版本 以後,我以爲是時候深刻理解一下爲何須要、何時以及如何使用這個庫。android

首先我注意到的是它的名字 —— 應用啓動,其代表這個庫的功能可能比它字面上的意義更普遍。這個庫並不涉及普通的啓動 (起碼目前如此)。它主要是爲了下降由 content provider 初始化致使的對應用啓動速度的影響。git

眼下您可能和我同樣歷來沒有考慮過第三方庫都是如何被初始化的。也許是由於全部這些處理過程都在底層完成。準確地說,您在 build.gradle 文件中添加了一行代碼來使一個開發庫做爲工程的依賴項,大功告成 (固然您還須要在工程中調用這個庫的 API,要否則您爲何要添加它呢?)。github

但是有不少庫並非簡單地封裝好一堆方法以供調用,它們時常還須要首先被初始化,而每每這個初始化仍是很耗時的過程。更糟糕的是,這其中還暗藏陷阱,由於這些庫經常在應用啓動的時候進行加載和初始化,究其緣由是因爲其內部使用了 content provider數據庫

敞開您的心扉 - Content Provider

Content provider 是 Android 中在不一樣應用之間共享數據的方式。舉個例子,手機中的聯繫人是經過 content provider 來實現數據共享的,這也使得其餘應用能夠訪問用戶的聯繫人數據 (固然,咱們假設用戶給予這些應用訪問聯繫人數據的權限)。您也一樣能夠爲其餘應用提供訪問受權,來使用您應用建立的數據。或許您的應用管理着一個 甜甜圈評分的數據庫,而做爲如此重要的信息,其餘應用可能須要頻繁地使用。app

只要一個應用經過任何一種方式聲明 content provider 開啓,此時就會自動建立而且啓動 content provider。編輯器

使用 content provider 有一個重要但可能並不那麼明顯的問題,就是應用在聲明 content provider 開啓後,它會被自動建立並運行。並且須要注意的是,一個應用的啓動並不僅是經過用戶啓動,其還能夠是經過系統訪問該應用的服務,又或者是 job scheduler 觸發了應用的一個循環做業等等。全部的這些都會觸發 content provider 的資源開銷以及產生相應的運算做業。當有須要訪問該 content provider 的時候,系統須要該應用可以處於就緒狀態,因此係統會在應用啓動的時候自動運行 content provider。ide

這些細節對於僅僅調用這些庫的開發者都是不可見的,由於具體實現都隱藏在自動生成的代碼中。您須要查看 合併後的 manifest 文件 來理解這一切是如何發生的。工具

合併 Manifest

我針對 Android 應用清單的交互操做基本上都發生在工程自生成的 Manifest.xml 文件中,我會經過編輯該文件來添加 activity、服務和權限。可是這個 manifest 文件並非最終提交到系統的那個,這個文件只是提供了關於您應用的信息,這些信息會被 "合併" 到最終的 manifest 文件中。合併後的文件包含了您的 Manifest.xml,以及編譯工具挑選的其餘信息,包括了您應用使用庫的 manifest 文件。也正是這個合併後的 manifest 文件告訴咱們庫的 content provider 究竟發生了什麼。性能

讓咱們來看一個具體的例子。並非全部的庫都使用了 content provider (儘管這仍是很常見的),因此咱們要用一個包含 content provider 的庫 -- WorkManager。爲了在個人工程中使用 WorkManager,我在應用的 build.gradle 文件中添加了以下依賴:測試

// 查看最新的版本號 https://developer.android.google.cn/jetpack/androidx/releases/work
def work_version = "2.5.0"
implementation "androidx.work:work-runtime-ktx:$work_version"

在我同步以及構建了該應用以後,我測算了一下啓動時間 (稍後會詳細介紹) 來對比添加這一依賴先後啓動時間上的差異。我注意到應用在添加依賴後,啓動時間比以前多了 70ms,並且這是在尚未調用 WorkManager 任何功能的狀況下,我只不過是添加了這個依賴。

我在合併後的 manifest 文件中發現了啓動時間延遲的緣由,您能夠在查看 Manifest.xml 文件時,經過點擊 Android Studio 編輯窗口左下方的 Merged Manifest 標籤來查看合併後的 manifest 文件。

編輯窗口下方的標籤控制着您所看到的是您應用的 manifest 文件仍是最終合併後的 manifest 文件

編輯窗口下方的標籤控制着您所看到的是您應用的 manifest 文件仍是最終合併後的 manifest 文件

在合併後的 manifest 文件中,我發現聲明 WorkManager 依賴增長了不少額外的信息,包括以下的 provider 代碼塊:

這個 provider 存在於添加 WorkManager 依賴後合併的 manifest 文件

這個 provider 存在於添加 WorkManager 依賴後合併的 manifest 文件

我很好奇這個 provider 是從哪裏來的,因此我點擊了其中的第一行,編輯器直接跳到 WorkManager 的 manifest 文件,其中包含以下代碼:

<application>
    <provider
        android:name="androidx.work.impl.WorkManagerInitializer"
        android:authorities="${applicationId}.workmanager-init"
        android:directBootAware="false"
        android:exported="false"
        android:multiprocess="true"
        tools:targetApi="n" />
    <!-- ... and a bunch of other stuff ... -->
</application>

合併的 manifest 文件的工做原理就是合併了組成應用的全部模塊的 manifest 文件,固然也包括我剛剛添加的 WorkManager 庫的 manifest 文件。由於其包含的 content provider 如今是合併的 manifest 文件的一部分,系統會在應用啓動的時候自動地建立並運行該 content provider。

如今我知道我是如何加載這個庫以及運行相關的 content provider。可是這究竟有什麼影響呢?

測算啓動時間

我最近發佈了一篇文章 - 測試應用啓動性能,其中詳細描述瞭如何測算應用的啓動時間。我用了一樣的方法來測算在添加 WorkManager 依賴先後的應用啓動時間,而且發現 WorkManager 增長了平均 67ms 的應用啓動時間。

請注意,正如我在 啓動測試 的文章中提到的,我鎖定了個人 Pixel 2 的 CPU 時鐘頻率,因此應用啓動時間在其餘用戶的設備上可能會短一些,而在另一些使用低端設備上會長一些。另外須要注意的是 (我也在那篇文章中提到),我可能並不須要鎖定時鐘頻率,由於系統一般會在應用啓動的時候以最高的頻率運行。可是鎖定時鐘頻率在性能測試的時候永遠都是一個好作法,由於這樣咱們才能得到穩定的結果。同時,鎖定時鐘頻率還一般會形成更長的運行時間 (因爲更低的頻率),這也會幫助咱們下降因爲太短運行時間形成的噪音數據。

還有一點須要強調的是,這個啓動時間並不全是 content provider 產生的。Content provider 確實會須要必定時間來建立,可是其須要大概 1-2ms 而不是我看到的 67ms。其實這是這個庫被加載以及初始化的總時間,外加建立和運行 content provider 的時間來初始化該代碼庫。

因此看起來僅僅是添加這個庫到個人項目就形成了將近 70 毫秒的啓動延遲。在一個真正的應用中,我可能會使用多個庫,而在應用啓動時它們中不少都有本身的 content provider 須要運行,這就會形成更嚴重的啓動延遲。

因此,咱們要作點什麼來減輕這個問題的影響呢?敬請關注咱們的後續文章,在下一篇文章中,我將深刻探討如何利用 AndroidX 的應用啓動 (App Startup) 庫來實現庫的延遲加載。

相關文章
相關標籤/搜索