說明: 本文由 AI(Claude Opus 4.7)根據我的初始筆記與想法完成。
我們的應用使用了許多元件,不管是自己寫的,或者是第三方提供的函式庫,有時不免就需要在應用啟動後做初始化的動作。而在 Android 的世界,很多時候呼叫這個初始化的方法都會放在 Application 內的 onCreate() 中。
class AppApplication : Application() {
override fun onCreate() {
super.onCreate()
AnalyticsSdk.initialize(this)
CrashReporter.initialize(this)
ImageLoader.initialize(this)
ExperimentClient.initialize(this)
// ... 以此類推
}
}
看起來稀鬆平常,不是嗎?但是很不幸的,這會造成應用啟動速度拉長。在我們的專案上,Firebase Performance 量到的冷啟動時間大約是三秒——意思是用戶按下 icon 之後,要等三秒才看到第一個畫面。連啟動頁都還沒顯示。
onCreate() 這麼敏感Application.onCreate() 跑在主執行緒上,而且是在系統能繪製你任何畫面之前。這段時間,用戶看到的是作業系統拉出來的空白視窗。你在這裡多呼叫一個 initialize(),用戶就多等同樣的時間。
更糟的是,這些 SDK 通常不會誠實告訴你它們的初始化要花多久。一個看起來人畜無害的呼叫,內部可能做磁碟 I/O、讀 SharedPreferences、甚至同步發網路請求。
不是每個模組都需要在 onCreate() 裡初始化。我的做法是把它們分成三類:
onCreate() 裡,但盡量選輕量的 SDK。onCreate() 裡 Thread {} 或 Executors.newSingleThreadExecutor() 丟到背景,不要卡主執行緒。把每個 SDK 想清楚歸在哪一類,就是最大的進步。
針對第三類,最簡單的做法是在 Activity.onResume() 第一次被呼叫之後觸發:
class MainActivity : AppCompatActivity() {
private var initialized = false
override fun onResume() {
super.onResume()
if (!initialized) {
initialized = true
window.decorView.post {
AnalyticsSdk.initialize(application)
ImageLoader.initialize(application)
}
}
}
}
用 decorView.post {} 的好處是:它會等到當前的 frame 畫完才執行,不會跟首幀搶資源。
如果你想要更結構化的做法,Jetpack 的 App Startup library 提供了 Initializer 介面,讓你集中管理依賴順序,並支援 lazy initialization:
class AnalyticsInitializer : Initializer<AnalyticsSdk> {
override fun create(context: Context): AnalyticsSdk {
return AnalyticsSdk.initialize(context)
}
override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
}
在 AndroidManifest.xml 把它標成 lazy,就不會跟著 Application.onCreate() 一起跑:
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="com.example.AnalyticsInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
之後在你需要的時候呼叫 AppInitializer.getInstance(context).initializeComponent(AnalyticsInitializer::class.java) 就好。
把初始化丟到背景聽起來簡單,但有兩個常見的坑:
CompletableFuture(或 Deferred、StateFlow),讓呼叫端能等它完成。Thread 跟主執行緒搶 CPU,在低階機器上效果有限。考慮用 Thread.MIN_PRIORITY 或 Executors.newSingleThreadExecutor() 並明確設定優先權。做這些優化前,記得先量基線。Firebase Performance 的「App start」trace、或 adb shell am start -W 都可以給你一個起點。優化之後再量一次——沒量就是憑感覺,憑感覺通常會騙自己。
在我們的專案上,把第三類搬出去之後,冷啟動時間有明顯縮短,使用者第一眼看到畫面的延遲也跟著下降。
Application.onCreate() 跑在主執行緒,而且擋住首幀。不要把它當成萬用 init 入口。decorView.post {} 或 Jetpack App Startup 的 lazy initializer。