App Startup

Photo by Tim Carey on Unsplash

對於用戶來說App的啟動初始化的時間越少越好,畢竟誰都不希望開個App去泡個咖啡,撒泡尿後再回來滑手機。那為了增進用戶體驗,Jetpack新增了優化啟動的Library,就是App Startup,目前還在alpha階段想要嘗試的朋友們可以試玩看看。

那有關於目前Startup的版本,可以參考這個連結startup Version

App-startup狀態

我們從Developers文件中看到下述這段說明。

App launch can take place in one of three states, each affecting how long it takes for your app to become visible to the user: cold start, warm start, or hot start. In a cold start, your app starts from scratch. In the other states, the system needs to bring the running app from the background to the foreground. We recommend that you always optimize based on an assumption of a cold start. Doing so can improve the performance of warm and hot starts, as well.

大意來說App啟動分成三種狀態cold start、warm start以及hot start。cold start就是App從頭啟動,另外兩種狀態就是App從管理後台移動到前台的啟動狀態。Developers也建議我們基於cold start進行優化,因為這麼做可以連帶改善warm、hot start。

Cold Start

cold start是系統從頭開始啟動創建應用程序的過程,它發生的時機點在你啟動Device首次啟用應用程序後,或者是系統殺掉這應用程式。相較於warm&hot start,系統和應用程式要做的工作多更多。

那要cold start,系統會執行三個任務:

  1. 加載並啟動App。
  2. 啟動後為App顯示一個空白畫面。
  3. 創立App的處理程序。(詳情請點我)

App進入處理程序之後,則會執行下列階段會做的事情:

  1. 創建App物件。
  2. 啟動主線程。
  3. 創建Main Activity。
  4. 依照xml中的元素將其創建進視圖層。(這個我不太會解釋,但行爲大概像StackOverflow的意思)
  5. 依照約束設置在畫面上。
  6. 執行初始化和繪製畫面。

一但處理程序完成第一次繪製,系統就會將當前的窗口替換成繪製完成的畫面,使用者就能對其進行操作,我們可以看看下方官網所提供的圖,讓你比較好瞭解整個流程的進行。

Cold Start App 流程圖 by Google Developers

那圖片內部的內容我就不多做敘述了,想了解詳情的話可以點右方的連結到Developers

Hot Start

與Cold Start相比Hot Start的開銷成本更低。它要做的就是將App從後台拉至前台,但如果被GC掉之後則需要響應啟動事件進行重新創建。另外它啟動流程也與Cold Start相同。

Warm Start

Warm Start包含了Cold Start部分啟動流程,同時啟動成本相較起Hot Start來的多,這樣解釋起來有點曖昧,但有許多狀態都是Warm Start,這邊引用官網中的例子幫助大家理解。

  • 用戶退出App,然後重新啟動它。啟動過程中必須調用onCreate() 從頭開始創建活動。
  • App被GC後用戶重新啟動它,啟動過程中調用已保存的狀態,傳遞給onCreate()。

Jetpack App Startup

瞭解了App的啟動狀態,回到我們的主題Jetpack App Startup對我們的App做了什麼事?它針對了我們的ContentProvider進行處理,原先每個Library都擁有獨立的ContentProvider進行初始化,就像下面圖示的樣子。

LibraryA, LibraryB, LibraryC個別使用ContentProviders初始化 by Husayn Hakeem
LibraryA, LibraryB, LibraryC統一使用一個ContentProviders初始化 by Husayn Hakeem

實作App Startup

那對App Startup有些許概念之後,我們就可以進入App startup的實作了!

Step 1

加入library

dependencies {
implementation "androidx.startup:startup-runtime:1.0.0-alpha02"
}

Step 2

實作初始化的接口,所以這邊要實作Initializer的function,我們就拿Lokalise SDK的Doc來做範例吧!

class LokaliseInitializer : Initializer<Unit> {  override fun create(context: Context) {
LokaliseSDK.apply {
init("<sdk token>", "<project id>", context)
setPreRelease(true)
updateTranslations()
}
}
override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
}

Step 3

接著在Xml補上我們的meta data,供InitializationProvider去check要啟動哪些項目。

那自動啟動與被動啟動的差別在哪邊呢?剛剛所示範的三個步驟都是自動啟動,意思就是你在InitializationProvider添加要啟動的meta data項目,在啟動流程就會自動為它初始化。

<application>
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<!-- 自動初始化 -->
<meta-data
android:name="com.example.initializes.LokaliseInitializer"
android:value="androidx.startup" />
</provider>
</application>

前面的三個步驟都是自動啟動的方式,接下來我們來說說被動啟動寫法步驟與上面大同小異,所以我這邊會以較快的方式帶過,也會寫另一種回傳初始化物件以及添加初始化順序的方式。

在create function裡面這裡進行初始化的工作,當然這裡也可以回傳你初始化的東西,寫成像下面這段就能在被動初始化時回傳你初始化的物件了。

class LokaliseInitializer : Initializer<LokaliseSDK> {  override fun create(context: Context): LokaliseSDK {
val lokaliseSdk = LokaliseSDK.apply {
init("<sdk token>", "<project id>", context)
setPreRelease(true)
updateTranslations()
}

return lokaliseSdk
}
override fun dependencies(): MutableList<Class<out Initializer<*>>> {
return mutableListOf(TimberInitializer::class.java)
}
}

dependencies function則會return需要初始化的列表,像上面這段範例就是LokaliseInitializer依賴於TimberInitializer,意思就是他會先進行TimberInitializer的初始化接著才會進行LokaliseInitializer的初始化。

完成了初始化實作接著就要在xml添加meta data,有看到我們在meta data中加入了remove,意思就是進用自動初始化,將它從初始化的meta data中刪除。

<application>
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">

<meta-data
android:name="com.example.initializes.LokaliseInitializer"
tools:node="remove" />
</provider>
</application>

接著就在你要初始化的地方添加這行,使用AppInitializer初始化它,當然照被動啟動範例來說Lokalise SDK初始化後會回傳LokaliseSDK。

AppInitializer.getInstance(context)
.initializeComponent(LokaliseInitializer::class.java)

總結

以上就是App在初始化可能進行的事情,當然還有更深的,但我們今天就著重於啟動狀態以及Jetpack所推出新的啟動方式,希望有幫助到各位~

參考

--

--

陳建維 Ben
工程師求生指南(Sofware Engineer Survival Guide)

喜愛新鮮知識充滿好奇心的Mobile工程師,3C愛好者也是書蟲。連絡信箱:tttw216@gmail.com;目前遷移至我的Blog: https://awilab.com/