Instagram Direct 的 Compose 遷移:讓列表架構、效能與驗證一起前進

目錄
把成熟的 Android 列表搬到 Jetpack Compose,最難回答的問題是:重寫之後,憑什麼相信使用者的體驗沒有退步?
Android Developers 與 Meta 在 2026 年 9 月 30 日發布的 Instagram Direct 案例,提供了一個具體切入點:讓同一組 Compose 元件同時支援 RecyclerView 與 LazyColumn,再用 feature flag 做正式環境 A/B 測試。Meta 另公布內測結果:一個 viewport 的 cache window 搭配可暫停 composition,相較原始 Compose,每分鐘大幅掉幀約減少 13%;只用 cache window 約減少 8%。原始案例
這裡有兩個不同層次的比較:容器遷移的 A/B 測試,以及 Compose 優化組合的內測。13% 不能解讀成「Compose 比 RecyclerView 快 13%」。原文也沒有提供該內測的裝置組成、樣本數與完整統計方法,因此無法直接預測其他 App 的收益。
本文的分析主張是:值得借鏡的,是把元件、容器與驗證拆成可獨立推進的工作。理解這個拆法,才知道哪些技巧適合自己的專案。
先讓元件能搬家,再決定何時更換容器
Android 官方的遷移策略建議漸進導入:新畫面採用 Compose,再逐步建立共用元件。這讓既有功能可以持續運作,也讓團隊有機會累積經驗。
本文的架構分析是:若目標包含未來更換列表容器,就需要先處理元件對容器的依賴。如果新的 UI 必須依賴舊 Adapter 的特殊欄位、綁定順序或回收時機,換了繪製技術,元件依然搬不走。這不代表每個專案都必須全面改成 Compose;長期混用是否合適,仍取決於維護成本與產品需求。
可以用下面的責任分工檢查自己的設計。這是根據案例整理的設計建議,並非 Instagram 的內部類別圖。
| 層次 | 負責的事情 | 檢查問題 |
|---|---|---|
| 狀態持有者 | 提供 UI 狀態、處理使用者事件 | 同一筆訊息的狀態是否有明確來源? |
| 共用 Compose 元件 | 根據輸入顯示內容、回傳事件 | 離開 RecyclerView 後,是否仍能使用? |
| 容器與轉接層 | 列表排列、綁定、生命週期整合 | 容器細節是否滲入元件的業務邏輯? |
| 實驗與量測 | 選擇實作、比較使用者體驗 | 切換容器時,是否也改了其他條件? |
共用元件的價值,是讓容器比較少一個變因。如果兩條路徑連訊息內容與互動邏輯都不同,測出差距之後,就很難判斷差異來自哪裡。
狀態的主人,不能由列表回收機制決定
官方的 state hoisting 指引提出一個明確原則:狀態提升到所有讀取與修改者的最低共同祖先,向下提供不可變狀態,以及修改狀態的事件。
這不代表所有狀態都得放進 ViewModel。只有元件自己使用的 UI 狀態,可以留在元件附近;涉及業務邏輯或跨元件協調時,再交給合適的持有者。
套用到列表遷移,本文建議優先問清楚:置頂、選取或展開狀態,究竟屬於哪一筆資料?元件被重新綁定、移動或離開 composition 後,哪些狀態需要保留?如果答案藏在可重用的物件欄位裡,就值得先檢查生命週期是否匹配。
混合 UI 還有另一個容易漏掉的細節。ComposeView 的預設策略 DisposeOnDetachedFromWindowOrReleasedFromPool 會辨識 RecyclerView 這類 pooling container:在池內的項目,不會只因自己離開視窗就立即釋放 composition;整個容器離開視窗,或項目被池捨棄時,才會依策略釋放。Fragment 則可使用 DisposeOnViewTreeLifecycleDestroyed,配合其 View 的生命週期。Compose in Views
因此,本文建議把「滑出畫面再滑回來」與「離開畫面再返回」分開驗證。兩者涉及的生命週期不同,只測第一次顯示,無法證明狀態與資源管理正確。
Cache window 決定提前做多少,pausable composition 決定如何分配工作
這兩項功能處理的是相連但不同的問題。Android 官方的效能解說指出,cache window 可用 viewport 比例或 dp 設定預先準備、保留的列表範圍;pausable composition 則讓 composition 的工作分散到多個 frame,並從 Compose 1.10 起預設用於 lazy layout prefetch。
提前準備更多內容,可以減少滑到項目時才開始工作的壓力;但遇到昂貴的項目,光是把工作提前,仍需要考慮單次工作的時間。可暫停 composition 處理的就是這個排程問題。
本文推論,調大 window 的代價也必須一起量測:保留更多項目會增加資源需求,而且提前做的工作未必都會被使用者看到。合理的目標,是在滑動順暢度與資源占用之間找平衡;「一個 viewport」只能當作實驗候選值。
還要注意文件版本。主案例的程式碼明確使用 Compose 1.13.0-alpha03,不能把其中的呼叫方式當作所有版本通用的範本。實作前應先核對專案依賴與對應 API;本文聚焦機制,不提供跨版本可直接貼上的設定。
預先組合之後,曝光紀錄也得重新檢查
一個項目進入 composition,並不等於使用者已經看見它。這在導入預先準備內容的機制後,尤其值得檢查。
Compose 的副作用文件說明,LaunchedEffect 與 composition 生命週期相關,離開 composition 時取消,key 改變時重新啟動;它並不是畫面曝光事件。這也不代表每一次預組合都會立即執行 effect,不能單靠預取範圍推算事件數。官方另提供可見性追蹤 API,讓應用程式依可見範圍與持續時間等條件處理事件。
以下是假設情境:App 把 composition 或 effect 的執行當作曝光,卻沒有檢查可見性。當預取、保留或重新進入 composition 的行為改變時,就需要驗證是否產生提早或重複計數。若出現這類誤計,轉換率的變化就混入了量測定義的影響,不能全部歸因於使用者行為。
因此,遷移驗收除了看畫面與速度,也值得檢查已讀、曝光與自動播放等事件的觸發條件。這些項目是導入建議,並非對 Instagram 現況的描述。
Baseline Profiles 處理另一種成本
滑動時的預先準備,無法取代首次執行程式碼的最佳化。官方說明,Baseline Profiles 讓 Android Runtime 對關鍵路徑進行提前編譯,減少解譯與即時編譯帶來的成本。Compose 自帶的 profile 主要涵蓋函式庫程式碼,應用程式仍需要針對自己的重要操作路徑產生與驗證 profile。Compose 的 Baseline Profiles 指引
本文建議,若遷移的是聊天列表,驗證流程就應涵蓋進入列表與實際操作,而非只停在 App 首頁。容器或關鍵操作路徑改變後,也應重新檢查 profile 是否涵蓋新的程式碼。
這同時解釋了為何不能拿 debug build 的手感決定遷移成敗。Android 官方要求效能評估注意 release mode、R8 與 Baseline Profiles;debug 的額外成本會干擾判讀。Compose 效能指引
把案例變成自己的實驗,而不是自己的承諾
在調整 cache window 前,先排除基本問題也很重要。官方建議避免在 composable 裡反覆進行昂貴計算,並為 lazy list 項目提供穩定、唯一的 key,協助框架辨識項目移動。效能最佳實務
接下來,本文建議用以下順序建立證據:
- 先固定比較條件。 使用一致的內容、圖片載入條件與操作流程,記錄裝置、版本、編譯及 profile 狀態。
- 先確認元件行為,再比較容器。 納入資料插入、項目移動、返回畫面與狀態恢復,避免效能改善掩蓋正確性退步。
- 逐項加入優化。 保留未調整的基準,再比較 cache window 等設定,避免同時改動太多條件而無法歸因。
- 把本機量測與正式環境觀察接起來。 本機用固定路徑重現問題,上線實驗則檢查真實使用情境,並預先定義停止條件。
工具方面,Macrobenchmark 的 FrameTimingMetric 提供 frame timing 的分布;其中 frameOverrunMs 在 Android 12(API 31)以上可用,正值表示超過 frame deadline,負值表示距離期限還有餘裕。觀察 P95、P99 能補上平均值遮蔽的長尾問題,但這些數值並不等同 Meta 的 LFDs/m,也不能單靠超時數值判定瓶頸就是 recomposition,仍需追蹤實際耗時來源。Macrobenchmark 指標文件
選擇工具之後,仍需要定義成功。本文建議至少一起看首次可操作時間、滑動長尾、記憶體與功能正確性。若一項改善伴隨另一項退步,決策需要產品需求與裝置分布,而不是單一漂亮的百分比。
遷移的終點,是團隊能解釋自己的結果
回到開頭的問題:重寫之後,憑什麼相信體驗沒有退步?
本文的判斷是,答案必須來自自己的證據。對需要逐步搬遷的複雜列表,共用元件與可切換容器提供了可操作的路徑;對缺乏狀態邊界或量測基準的專案,先把這兩件事補齊,更有助於判斷下一步。
13% 是一個值得追查機制的起點。真正需要帶回專案的問題,是我們是否知道工作何時發生、狀態由誰持有,以及使用者體驗如何被量測。這些問題有了答案,Compose 遷移才有可驗證的方向。
資料查核日期:2026 年 10 月 16 日。封面為 AI 生成的概念插畫,不是 Instagram 畫面、內部架構圖或效能量測結果。


