軟體工程師
今年,Android 正式改為優先使用 Compose 建構所有 UI,並將舊版 View 設為維護模式。Google Play 早在 2020 年就開始這項工作。在近二十年的工程歷程中,我們服務了數十億使用者,並透過經過驗證的最佳做法、與核心 Compose 團隊密切合作,以及持續演進的工具,成功遷移至 Jetpack Compose。
瞭解如何使用 Compose 翻新 Google Play,大幅提升速度和長期穩定性。本課程將逐步說明如何將具備代理功能的 Figma 設計工作流程與 Compose 實作連結、自動化品質控管以避免不必要的重新組合,以及運用動態色彩、豐富的字體排版和分層形狀,打造富有表現力的介面。您將學到經過驗證的策略,能更快分類錯誤,並有信心擴大 Compose 遷移規模。
ComposeView。MaterialTheme 疊加層和 Material Color Utilities 程式庫。這可避免 Activity 終止,但在建構時產生色彩配置,有助於限制執行階段延遲。BoxWithConstraints。轉錄稿
大家好!
我是 Jon。
我是 Google Play 商店團隊的成員。
這位是我的同事 Kirill。
今天我們要談談如何將大量程式碼從 View 和 Fragment 系統移至 Compose。
讓我們開始吧!
那麼,採用過程如何?規模有多大?
自 2007 年左右起,我們的應用程式程式碼集就自然而然地成長。
我們有數百萬行正式環境程式碼、200 個畫面,以及 500 個不重複的 UI 元件,這些元件會用於許多不同的畫面。
2020 年,我們發現開發人員速度、可靠性和效能開始無法達到預期目標。
因此,我們決定採用現代化的 UI 技術堆疊。
我們評估了許多不同的程式庫。
當時,我們也和 AndroidX 的一些朋友討論 Compose,並決定採用 Compose,成為 Compose 的第一個主要合作夥伴。
還順利嗎?
六年後,Play 商店應用程式中 97% 的使用者曝光都發生在僅限 Compose 的畫面上。
抱歉,自 2020 年起,所有手機和大型螢幕開發作業都已改為僅限 Compose。
自 2025 年起,我們甚至將 XR、TV、Auto、Wear 等擴充板型規格也轉移至 Compose。
Compose 是我們能成功的重要推手,這也是我們持續採用更多 Compose 的原因。
他們擁有非常強大、全面的測試套件,可協助我們每週兩次採用新版 Compose。
我們導入了 Alpha 版和新 API,因此能利用新的錯誤修正和效能改善項目,並真正探索部分新 API,提供新的使用者體驗。
特別是效能方面,Compose 確實為我們應用程式的所有主要畫面,持續提供流暢的效能。
包括搜尋結果網頁、首頁和詳細資料頁面。
效能、卡頓和載入延遲時間,對我們的業務和使用者體驗來說,都是非常重要的指標。
因此,過去 14 個月內,我們看到 30% 的改善。
但這一切究竟是怎麼開始的?
這才是我們今天的主題。
接下來,我們來談談遷移計畫。
因此,Play 採取多步驟做法。
我們不想孤注一擲,
我們最後採取了幾個步驟,瞭解風險和對業務的影響,並在全力投入前建立信心。
我將這些階段命名為:爬行、步行、慢跑和跑步。
因此,我們在起步階段的目標是熟悉 Compose。
我們一開始先從流量較低的網頁著手。
例如設定頁面、第三方授權等。
您的主要目標是熟悉 Compose 程式庫。
因此,這可以習慣資料列、資料欄、文字、圖片等所有這類內容。
接著,您需要開始導入所有應用程式基礎架構,並瞭解其在 Compose 中的運作方式。
包括曝光記錄、點擊記錄、主題設定,以及資料載入。
所以上述所有因素都有影響。
我們發現,確保所有畫面看起來一致,是很有幫助的做法。我們建構了一些單元測試和功能測試,檢查 Compose 中的主題設定物件是否直接對應至 XML 樣式、字體排版、顏色等所有項目。
因此,當我們熟悉並感到自在後,就決定開始步行。
對我們來說,這是在流量非常高的畫面上,採用較小的元件,並評估這類遷移作業的情況。
建議您採用 RecyclerView 中的項目,這樣就能瞭解 View 和 Compose 系統之間的相容性問題,並在過程中查看測量效能和卡頓情形。
元件本身不一定要非常複雜,但您確實需要一些能練習版面配置、文字、圖片等內容的東西,以及一些您通常會看到的各種不同例項。
在這個領域中,您應該注意並預期會發生的情況是,卡頓和延遲時間會稍微增加,但這是因為您現在執行兩個堆疊。
您有一個完全在 Compose 中的轉譯堆疊,以及一個在 View 中的轉譯堆疊。
因此會發生記憶體顛簸、載入額外類別等情況。
但隨著您採用越來越多 Compose,這種情況就會消失。
如果真的需要,可以稍微預熱該畫面,讓畫面符合需求,並在短期內降低成本。
所以,在步行之後,我們轉為慢跑。
這表示我們已經非常熟悉這項技術。
不要只在高流量網頁上測試單一元件,而是測試整個高流量網頁。
因此,我們仍保留導覽程式庫並使用片段,但每個畫面都完全以 Compose 實作。
我們剛才讓 Fragment 傳回 ComposeView 本身。
因此,您可以預期延遲和卡頓情形會有所改善。
您不會遇到我剛才提到的記憶體顛簸和類別載入問題。
Compose 本身也會在 ComposeView 層級進行某些層級的快取。
因此,如果 RecyclerView 有許多外觀一致且可能具有相同圖示的資訊卡,則所有圖示剖析和資源使用情形都會在 ComposeView 中的所有項目之間共用。
所以之後我們就開始跑步。
這時你就要全力以赴。
當應用程式的大部分流量都來自 Compose 時,我們就會向上移動一個層級。
因此,在畫面之上,我們有 Activity。
因此,請將 ComposeView 移至 Activity 根層級。
許多程式碼看起來會很相似。
您仍會嘗試執行導覽指令,例如從一個畫面切換到另一個畫面。
但您不是執行片段交易,而是呼叫可組合函式。
無論選擇 Nav2 還是 Nav3 程式庫,都可以在其基礎上進行這項操作。
我們並未遷移所有畫面。
我說過,我們有很多這類工具。
因此,我們建構了 Fragment 相容性,讓您可以使用以 Fragment 為基礎的畫面,並利用 Compose 的 AndroidFragment 包裝函式來執行這項操作。
再次強調,卡頓和延遲對我們的業務至關重要,因此我們不斷挑戰極限,您也會看到這方面的改善,因為我們現在有跨螢幕快取,記憶體抖動問題也大幅減少。
但也會獲得新功能。
右側的 GIF 範例顯示,您可以在畫面之間進行共用元素轉場。
因此,當您從一個畫面移至另一個畫面時,圖片會放大,提供連續性。
在整個轉換過程中,您也可以充分運用 Compose 的動畫彈性。
接下來,我將交給 Kirill 介紹更多功能。
謝謝 Jon。
我想談談一個我非常重視的主題,那就是 Android 應用程式的主題設定和樣式設定,這是我一直以來都在做的事情。
在 View 的世界中,有 Java 程式碼,稍後還有 Kotlin 程式碼,然後是 X-- XML 資源,例如顏色、字串、尺寸、版面配置、主題和樣式。
運作方式是將所有 XML 資源編譯成 APK 內含的資源資料表,而且幾乎是靜態的。
您可以在執行階段動態使用 API Resources.Theme.applyStyle,在 UI 階層頂端套用自訂樣式。
主要限制是您必須在加載內容前完成這項操作,也就是說,如果您想在現有的 UI 上動態套用該樣式,就必須拆除並重新建立整個 Activity,這對使用者體驗來說並不好。
此外,由於系統會套用這些預先編譯的 XML 區塊,因此根據實驗旗標或任何其他業務需求,將多個條件式動態疊加層相互結合,會變得有點困難。
在 XML 世界中,很難建立適當的樣式階層心智模型。
最後,Android Studio 或任何其他開發人員工具都沒有單一整合檢視畫面,可讓您在 Java、程式碼和 XML 之間架起橋樑。
這有點難以兼顧,也難以建立適當的心智模型,以及偵錯為何未套用樣式、為何顏色錯誤等問題。
在 Compose 的新世界中,特別是 Material Design 系統和 Compose Material 程式庫,以及其他設計系統,我們有 Material Design 系統,也有 MaterialTheme 可組合函式,您可以將其套用至螢幕上 UI 階層的最頂層,也可以將此 MaterialTheme 做為疊加層套用至 UI 的任何較小部分。
右側畫面頂端顯示 Play Points 黃金級,這是一張小卡片,上面有進度指標和一些文字。
我們使用 Material Color Utilities 程式庫產生的自訂色彩配置,套用這個 Material Design 主題疊加層,您可以在 GitHub 上使用。
我們傳入類似這種棕黃色、金色的顏色做為種子,然後產生完整的 Material 顏色符記,主要顏色在主要顏色上,次要顏色在次要顏色上,然後我們在實作這張卡片時使用這些顏色。
Android 開發人員也能充分運用動態主題功能。
所有作業都是透過程式碼完成,而且都是在 Kotlin 中完成,這表示您可以更輕鬆地建立適當的心智模型,瞭解所有作業的運作方式,也更容易偵錯。
在內部,這些疊加層會附加為組合本機,套用至階層中您選擇的特定 UI 節點。
效能考量:使用 Material Color Utilities 程式庫產生完整色彩配置並非免費。
不過,這會產生一定的負擔。
因此,如果您的產品需求較為複雜,需要多個這類元件 (例如便當盒等),且在執行階段產生多種色彩配置,就會開始增加延遲和卡頓。
因此,您需要:a. 開始評估這對效能的影響;b. 將部分負擔從執行階段轉移至建構階段。
您可以在建構時使用相同的 Material Color Utilities 程式庫,根據品牌或產品需求預先產生這些色彩配置,並將這項負擔轉移至建構時間。
我提到用來追蹤疊加層在 UI 階層樹狀結構中確切位置的組合區域,這些區域的成本較低,但仍非免費。
因此,視您使用的數量而定,數十個、數百個,就會開始增加。
在某些情況下,您確實需要開始考量所有這些組合式本機的成本。
這就是我提到的 Compose 真正的動態執行階段主題設定功能。
這裡有四個象限,相同的視覺效果,相同的事件資訊卡。
這張主頁橫幅橫跨整張資訊卡,左上角有標籤,右下角有按鈕,還有一些文字。
在每個象限的右側,您可以看到顏色符記的標註,這些符記是每個元素的設計規格。
這四個項目的資訊卡實作方式完全相同。
唯一的差別在於,包裝資訊卡的 Material Design 主題設定疊加層,是根據圖片的主要顏色 (藍色、紫色、黃色和紅色),以動態建立的色彩配置生成或設定。
其他部分則維持不變。
您不必拆除並重新建立活動。
您可以根據實驗旗標或任何其他業務需求,有條件地套用。
如前所述,這是一種更動態、真正動態的方式,可將自訂色彩套用至應用程式。
現在請 Jon 繼續介紹下一個部分。
是啊
這就是 Kirill 提到的靈活性。
我們來談談可靠性和速度。
因此,所有開發人員和企業都希望快速建構高品質程式碼。
接下來,我們來看看 Compose 如何協助 Play 達成這個目標。
首先,我知道這張圖表很大,但簡化架構對所有程式碼作者和維護人員 (無論是人類還是代理) 都有很大的好處。
我不會深入探討這個問題,但 Compose 嘗試提出的其中一個心智模型是單向資料流,這對我們的團隊產生了很大的影響。
如果不熟悉運作方式,基本概念是您有一個資料來源。
資料來源會提供資料。
這類資料可能會轉換為特定 UI,並以 UI 模型的形式輸出。
Compose 只要呼叫該狀態的值屬性,就能輕鬆觀察到這項資訊。
這就像是圖表頂端的箭頭。
事件則會反向流動。
因此,如果使用者點選按鈕,系統會返回 UI 轉換器,更新資料來源並發出新值,然後執行轉換,形成這個迴圈。
因此,所有內容都會透過這個相對簡單的迴圈執行。
我們發現,這項做法有助於縮短適應期。
您不必再擔心這些呼叫是在主執行緒上發生,還是從其他執行緒上發生。
它遵循一個一般迴圈。
Compose 讓資料觀測變得非常簡單,且資料在各架構層之間具有強大的同步一致保證。
這個心智模型減少了團隊中許多關於可靠資料來源的討論,並完全消除了幾種競爭狀況。
另一個巨大優勢是,與我們之前的模型檢視控制器架構相比,平行執行資料來源和 UI 轉換變得非常簡單。
因此,這確實改善了商店內的載入延遲時間,大約縮短了 50 毫秒。
這讓我們印象深刻。
除了單向資料流,Compose 的另一大優勢是宣告式 UI。
Kirill 稍早有提到這點。
因此,Play 開發人員也對這項功能讚譽有加。
UI 控制流程現在位於單一檔案中。
右側範例相當簡單明瞭。
@Composable
fun PurchaseRow(onClick: () -> Unit, modifier: Modifier) {
Row(modifier) {
Text("20% off sale")
if (LocalExpts.current.useButtonV2) {
ButtonV2("Buy", onClick = onClick)
} else {
ButtonV1("Buy", onClick = onClick)
}
}
}我們會根據實驗旗標,在兩種按鈕實作方式之間切換。
您不必閱讀多個檢視畫面檔案、片段檔案和 XML 檔案,只要從上到下閱讀即可,這樣會簡單得多。
巢狀可組合項的成本也遠低於巢狀檢視區塊階層,因此我們能自然地安排程式碼,提高可讀性,而不必為了效能而費盡心思。
我們發現的其他好處是,生命週期回呼變得更簡單,需要擔心的數量也減少了。
因此,您不必使用檢視畫面生命週期、片段生命週期、活動生命週期,而是使用可處置效果、副作用,並根據您放置的位置,以非常一致的方式運作。
這對我們來說是一大優勢。
因此,我認為這兩項架構變更為 LLM 奠定了基礎,使其能生成相當高品質的程式碼。
相信許多人可能都試過,或用過自己最喜歡的大型語言模型。
您可以擷取螢幕截圖,並加上顏色和字體排版註解,然後將其輸入,看看它是否能生成資料列、資料欄、文字和按鈕,而且效果相當不錯。
不過,這項功能仍有其限制。
Play 其實不瞭解您的程式碼集,對吧?
它不知道您已建構哪些元件,也不知道設計權杖名稱。
只會提供硬式編碼的顏色和字串。
哪些內容應來自裝置、哪些內容應來自伺服器,或如何確保使用 LazyList 時效能良好,而不只是建立大型資料欄?
因此,為瞭解決部分問題,我們在這些螢幕截圖中加上更多中繼資料。
包括 UI 名稱、定界框 (用於判斷元件的起點和終點)、設計符記 (用於顏色和字體排版的語意名稱)、技能,以及一些提示 (用於說明元件的放置位置,以便考量效能)。
以及非常高階的函式簽章,以便區分動態和靜態內容。
因此,您最終會得到類似我們之前的結果,然後移至之後。
這裡沒有定界框,因為這會為圖表增加許多雜訊。
但您可以看到這些硬式編碼的顏色和字體排版名稱,已替換為 title large 和 on surface。
我們清楚標示了資訊卡標題和圖示徽章等元件名稱。
這樣一來,您就能在程式碼集中找到這些內容。
對我們來說,許多這類元件也來自常見的設計程式庫,Kirill 稍後會說明。
因此,您不需要一直為所有產品加上自訂標籤。
接著,您將其與方法簽章配對,如我們在底部 OfferCard 所示,然後它就會知道標題、endDate、圖片都是動態的。
不應硬式編碼。
有了這些語意名稱,我們現在可以切換到 Kirill 提到的所有不同顏色應用程式。
因此,我們生成了更多程式碼。
接下來,我會將時間交還給 Kirill,由他說明我們如何維護品質。
好
謝謝 Jon。
接著,我們繼續探討 LLM 和 Agent Assist 輔助開發,首先從使用者體驗設計開始。
無論您選擇哪種設計工具 (例如 Figma 或其他工具),都應建立一個共用範本,供所有設計師使用。這個範本應包含您所選設計系統中的顏色、字體排版和形狀符記。
也許是 Material。
或許是自訂設計系統。
您希望這些範本內建支援功能,可切換淺色和深色模式,以及切換一般、中等和高對比模式 (幾年前在 Android 層級推出)。
這樣一來,設計師就能快速切換這些不同的組合,查看設計規格的反應。
接著,在匯出管道中,當設計定案並交給實作端時,您需要具備某種能力,追蹤每個顏色、字體排版元素、形狀是否都使用相符的權杖,而不是硬式編碼的 RGB 值、字型系列或字型大小。
最後,我們發現 Google Play 規模非常實用,我們甚至沒有數十個,我們有幾百個元件、叢集、資訊卡、容器,可用於各種螢幕。
我們發現,將所有資訊集中在一個地方 (我們稱為元件目錄) 追蹤,然後根據該元件目錄訓練自訂代理,是非常實用的做法。
當您開始設計新畫面、新使用者歷程或新產品領域時,這個代理程式就派得上用場。您可以將初步設計探索成果 (例如投影片或 Figma 規格) 饋送給代理程式,代理程式就會快速找出已存在的元件,這樣就不必重新設計、重新制定規格,也不必從頭開始重新實作。
這能大幅降低設計和實作成本。
在實作方面,我們非常仰賴核心和自訂 Lint 規則。
我想特別介紹幾個核心 Lint 規則,其中 FrequentlyChangingValueDetector 可協助找出不必要的重組來源,避免造成延遲和卡頓;另一個是 ScaffoldPaddingDetector,可標記使用 Material Compose Scaffold 可組合項,但忽略傳入邊框的情況。
這不是延遲和卡頓的來源,但在某些情況下,可能會導致難以偵錯的問題,例如在無邊框或折疊、展開等情況下,版面配置不一致。
最後,BoxWithConstraints 則有特定的效能考量。
可能需要兩個影格才能正確配置所有內容。
我們不會直接封鎖,但會標記為 Lint 警告,讓開發人員知道他們正在選擇使用 BoxWithConstraints,或可以選擇其他版面配置。
主題設定自動化功能也一樣,有顏色、字體和形狀符記。
我們有一項 Lint 規則,會禁止使用硬式編碼顏色和硬式編碼字體排版。
我們有一項 Lint 規則,會標記 Material 2 元件的使用情形。
我們只想使用最新、最棒的 Material 3,包括去年和今年推出的全新表現力元件。
我們有一項自訂 Lint 規則,可防止使用者從舊版 XML 檔案讀取顏色。
我們希望新畫面能優先使用 Compose,且只使用 Compose。
此外,文字可組合項 (特別是 Material Design 設計系統中的文字可組合項) 具有樣式和顏色的備用設定。
系統會從本機內容顏色和本機字體排版讀取,但我們不想依賴這項功能。
我們希望每段文字都能刻意使用設計規格中的特定樣式、字體樣式和特定顏色。
這就是我們提供的 Lint 規則。
最後,與 ScaffoldPaddingDetector 類似,我們有一項自訂 Lint 規則,強制要求每個可組合函式都使用其輸入修飾符。
同樣地,這也是為了避免新用途不當放置,或不尊重傳入的背景等。