Android Jetpack組件之App Startup

寫在前面

在看Jetpack的官網時,發現官方在Jetpack中新加了一個App Startup組件,查了一下是前幾天和HiltPaging3一起更新的,其他的組件還沒看。

官方網址: https://developer.android.com/topic/libraries/app-startup

要選擇語言爲ENGLISH哦,中文版暫時還沒有這個頁面。

爲什麼需要App Startup呢?

在我們實際的開發工作中,一些第三方庫需要在App啓動的時候初始化並不少見,比如WorkManagerLifeCycle在App啓動時通過ContentProvider進行初始化。通過ContentProvider,一旦App冷啓動後,在調用Application.onCreate( )之前,ContentProvider就可以自動執行初始化。

App Startup是什麼?

按照Google官方給出的定義:App Startup這個庫在App啓動的時候提供了一個直接、高效的方式來初始化組件。所有的library開發者和app開發者能夠使用App Startup這個組件來簡化啓動順序並且顯示地設置初始化順序。通過App Startup這個組件,您無需爲需要初始化的每個組件定義單獨的content providers,而是允許通過共享一個content provider來定義組件初始化器,從而提高應用的啓動速度;

看完上面的話,我們知道,App Startup提供了一個ContentProvider來完成項目需要的一些組件的初始化,避免每個第三方的庫(比如友盟統計、埋點等)單獨通過ContentProvider進行初始化。

我的理解是通過App Startup這個組件,我們可以將所有第三方需要在Application中初始化的一些庫都通過ContentProvider來初始化,有點偏向於將第三方庫初始化這個過程進行了封裝,大概是這個意思。

如何使用App Startup?

我們可以通過定義組件初始化器完成組件的初始化,那麼如何定義組件初始化器呢?Android官方爲我們提供了Initializer<T>接口,通過實現接口並實現接口中的兩個方法就可以實現組件初始化器的定義了。

我們來看下這兩個方法:

  • create() : 包含了初始化組件,並且返回T的實例的所有必要操作;

  • dependencies() : 此方法返回一個初始化程序依賴的其他Initializer對象的列表。可以使用此方法來控制應用程序啓動時初始化的順序。

在沒有使用App Startup的時候,如何保證content providers的初始化順序呢?其實很簡單,在配置清單中將先啓動的content provider<meta-data>標籤放在前面即可。

下面我們來看下如何進行初始化。

通過App Startup來運行依賴項的初始化有兩種方式:

  • 自動初始化(automatic initialization)

  • 手動初始化(manually initialization)

無論是自動初始化還是手動初始化都需要在app或者library中的build.gradle文件中添加如下依賴:

實現自動初始化(拿官方例子來看)

假設你的應用程序依賴了WorkManager,並且需要在程序一開始啓動時就初始化WorkManager,定義一個WorkManagerInitializer類並且實現Initializer<WorkManager>接口:

如圖中所示,dependencies()方法返回了一個空列表,意思是我WorkManager實例化誰也不需要依賴,我自己個就能行。 假設我們的應用依賴了另一個叫做ExampleLogger的庫,這個庫依賴於WorkManager。這也就意味着,初始化這個庫必須先確保WorkManager的實例已經被初始化了纔可以。那麼如何做呢?我們看下官方代碼:

代碼中定義了一個ExampleLoggerInitializer類並且實現了Initializer<ExampleLogger>接口。這個時候我們看到dependencies()方法返回的就不是空列表了,而是包含了WorkManagerInitializer的一個列表,這樣ExampleLogger要想初始化,必須先初始化WorkManager

提示:如果App中之前使用content providers來初始化應用程序中的組件,請確保是使用App Startup時刪除這些content providers

App Startup包含了一個名爲InitializationProvider的特殊的content provider,它用來找到並且調用你的組件初始化器。那麼這個過程是什麼樣的呢?

  • 首先,通過檢查InitializationProvider清單標籤下的<meta-data>標籤,找到組件初始化器;
  • App Startup調用它找到的所有組件初始化器的dependencies()方法。

這就意味着如果想要讓App Startup找到組件初始化器,必須滿足下面的一個條件:

  • 組件初始化器在InitializationProvider清單標籤下配置了相應的<meta-data>標籤;
  • 組件初始化器在一個已被找到的組件初始化器的dependencies()方法中被列出;

從我們上面寫過的WorkManagerInitializerExampleLoggerInitializer這兩個例子中來說,爲了確保初始化器能被實現,需要在清單文件中配置如下代碼:

如圖中所示,我們只配置了ExampleLoggerInitializer<meta-data>標籤,因爲它是依賴於WorkManager的,並且滿足了第二條:dependencies()方法中列出了它依賴於WorkManager,如果ExampleLoggerInitializer能被炸到,那麼WorkManagerInitializer一定也能被找到。

tools:node="merge"屬性是爲了確保清單合併工具可能造成的衝突問題

App Startup庫包含了一系列的lint規則,通過這些規則,你能夠檢查是否正確定義了組件初始化器。你可以在終端通過./gradlew :app:lintDebug命令執行lint檢查。

實現手動初始化

通常來說,當你使用App Startup時,InitializationProvider對象就會使用AppInitializer在App啓動時來自動尋找並且運行初始化器。然而,你也可以直接調用。AppInitializer來手動初始化不需要在啓動時就調用的組件初始化器。這個操作被稱作懶初始化,它能夠減少程序啓動的時間。要想實現手動初始化,必須先禁止掉你想要手動初始化的組件的自動初始化功能。 爲了禁止掉單個組件的自動初始化功能,可以在清單文件中移除那個組件的<meta-data>標籤,舉例來說,我們想禁止ExampleLogger的自動初始化:image

使用tools:node="remove"而不是直接移除這個標籤是爲了確保清單合併工具能夠移除所有合併文件的這個標籤。

提示: 禁用組件的自動初始化也會禁用該組件的依賴項的自動初始化,比如我禁用了ExampleLogger,而ExampleLogger依賴了WorkManager,那麼WorkManager也不會自動初始化了。

我們現在知道如何禁止單個組件的自動初始化,那麼如何禁止全部組件的自動初始化,轉而手動初始化呢?Android官方給我們提供了這個寫法:

禁用自動初始化後,你可以使用AppInitializer手動初始化組件和它的依賴。還是看下官方代碼:

通過上述代碼,App Startup也對WorkManager進行了初始化,因爲ExampleLogger依賴了WorkManager

源碼分析

App Startup包中代碼並不多,只有五個類

其中最核心的類就是InitializationProvider,它是繼承了ContentProvider,這樣我們就懂了,在onCreate()方法中,可以看到它其實是調用了AppInitializer這個類中的discoverAndInitialize()方法,我們簡單看下這個代碼:

代碼很簡單,就是解析出來metadata中的數據,然後遍歷metadata拿到配置的初始化器,然後調用每個初始化器的初始化方法,也就是doInitialize()方法。接下來再看下這個方法:

可以看到在執行初始化的時候,先判斷了是否有依賴項,有的話先執行依賴項的初始化。

參考資料

寫在後面

關於App Startup的內容暫時就這麼多了,如果你正在用Jetpack,那不妨試用這個組件吧。