iT邦幫忙

2025 iThome 鐵人賽

DAY 9
0
Mobile Development

Android 性能戰爭:從 Profiler 開始的 30 天優化實錄系列 第 9

# Day 9:【啟動戰役】Application 的瘦身計畫

  • 分享至 

  • xImage
  •  

各位戰士,早安!昨天我們已經學會了如何精確測量敵人的實力(啟動耗時)。今天,我們將發起第一次外科手術式的打擊,目標是冷啟動過程中最關鍵的瓶頸——Application.onCreate() 方法。

為什麼是它?因為在冷啟動時,Application.onCreate() 是系統建立你的應用程式進程後,第一個執行的你自己的程式碼。它在主執行緒 (UI Thread) 上運行,並且會完全阻塞後續 Activity 的建立和渲染。簡單來說,它每多執行 1 毫秒,你的使用者就得多盯著白屏 1 毫秒

你的 Application 是否過於臃腫?

許多開發者習慣將各種第三方函式庫的初始化、全域物件的設定等工作全部塞進 Application.onCreate()。這是一個非常危險的習慣。檢查一下你的程式碼,是否存在以下「壞味道」:

  • 初始化過多的 SDK:分析、廣告、崩潰回報、推送通知... 每個 SDK 的初始化都可能包含 I/O 操作或耗時的設定。
  • 建立複雜的依賴注入圖:使用 Dagger/Hilt 等框架時,在 Application 中建立完整的依賴圖可能非常耗時。
  • 執行 I/O 操作:讀取 SharedPreferences、預載入資料庫、解壓縮資源檔等。
  • 執行複雜的計算:任何非必要的 CPU 密集型任務。

我們的目標是:讓 Application.onCreate() 變得極度輕量,只做最最必要的事情。


我們的瘦身利器:App Startup 函式庫

為了解決這個問題,Google 在 Jetpack 中為我們提供了一把鋒利的手術刀——App Startup 函式庫。

App Startup 提供了一種高效、解耦的方式來管理應用程式啟動時的元件初始化。它的核心思想是:

  1. 分離關注點:將每個元件的初始化邏輯封裝到各自的 Initializer 中,而不是全部堆在 Application 裡。
  2. 定義依賴關係:可以明確指定某個元件的初始化必須在另一個元件之後執行。
  3. 實現懶加載 (Lazy Loading):可以選擇讓某些元件在第一次被使用時才進行初始化,而不是在 App 啟動時就全部載入。

實戰演練:從混亂到有序

瘦身前:臃腫的 Application.onCreate()

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        // 1. 初始化日誌工具
        Timber.plant(Timber.DebugTree())

        // 2. 初始化分析工具 (假設它依賴日誌工具)
        AnalyticsSDK.initialize(this)

        // 3. 初始化一個耗時的資料庫 (假設它不依賴任何人)
        HeavyDatabase.getInstance(this)
    }
}

這個寫法有幾個問題:所有東西都在主執行緒上同步執行,而且初始化邏輯緊密耦合在 Application 類別中。

瘦身後:使用 App Startup

步驟 1:加入依賴
app/build.gradle.kts 中加入 App Startup 的依賴:

implementation("androidx.startup:startup-runtime:1.1.1")

步驟 2:為每個元件建立 Initializer

// 1. 建立日誌工具的 Initializer
class TimberInitializer : Initializer<Unit> {
    override fun create(context: Context) {
        Timber.plant(Timber.DebugTree())
    }
    // 沒有依賴其他元件
    override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
}

// 2. 建立分析工具的 Initializer
class AnalyticsInitializer : Initializer<AnalyticsSDK> {
    override fun create(context: Context): AnalyticsSDK {
        // 假設 AnalyticsSDK 初始化後會返回一個實例
        return AnalyticsSDK.initialize(context)
    }
    // 明確聲明它依賴 TimberInitializer
    override fun dependencies(): List<Class<out Initializer<*>>> = listOf(TimberInitializer::class.java)
}

// 3. 建立資料庫的 Initializer
class DatabaseInitializer : Initializer<HeavyDatabase> {
    override fun create(context: Context): HeavyDatabase {
        return HeavyDatabase.getInstance(context)
    }
    // 沒有依賴
    override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
}

步驟 3:在 AndroidManifest.xml 中註冊

這是最關鍵的一步。我們透過 provider 標籤來註冊這些 Initializer,並完全移除 MyApplication 中的初始化程式碼。

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

    <!-- 註冊 TimberInitializer -->
    <meta-data android:name="com.example.TimberInitializer"
        android:value="androidx.startup" />

    <!-- 註冊 AnalyticsInitializer -->
    <meta-data android:name="com.example.AnalyticsInitializer"
        android:value="androidx.startup" />
        
    <!-- 註冊 DatabaseInitializer -->
    <!-- 如果某個元件不是啟動時必須的,可以將其設定為懶加載 -->
    <!-- 這裡我們假設資料庫不是,所以移除它的 meta-data 來實現懶加載 -->
    <!--
    <meta-data android:name="com.example.DatabaseInitializer"
        android:value="androidx.startup" />
    -->
</provider>

完成後,你的 Application.onCreate() 就可以變得乾乾淨淨!App Startup 會自動按照你定義的依賴順序,在 ContentProvideronCreate 階段完成所有初始化工作。
今日總結
今天,我們對 Application.onCreate() 進行了一次成功的瘦身手術。

  • 我們認識到 Application.onCreate() 是冷啟動的關鍵瓶頸。

  • 我們學會了使用 App Startup 函式庫來解耦和管理初始化任務。

  • 我們掌握了透過定義 Initializer 和在 AndroidManifest 中註冊,來實現自動化、有序的初始化流程。

透過這場手術,我們不僅讓 Application 類別的程式碼變得更清晰,更重要的是,我們為後續更細緻的優化(例如將某些任務移出主執行緒)打下了堅實的基礎。

Application 階段的戰役暫告一段落。明天,我們將把戰線推進到 Activity 層級,探討如何優化第一幀的繪製,讓使用者能更快地看到有意義的畫面。

我們明天見!


上一篇
# Day 8:【啟動戰役】定義與測量:冷啟動、熱啟動與溫啟動
系列文
Android 性能戰爭:從 Profiler 開始的 30 天優化實錄9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言