Compose 會將類型視為穩定或不穩定。穩定項目是指本身不可變動,或 Compose 能知道其值是否已在重組之間變更。不穩定項目是指 Compose 無法知道其值是否已在重組之間變更。
Compose 會根據可組合函式參數的穩定性,判斷如何比較輸入內容,並決定是否可在重組期間略過可組合函式 (從 Kotlin 2.0.20 開始,預設啟用強化略過模式):
- 穩定參數:Compose 會使用結構相等性 (
Object.equals()) 比較穩定參數。如果可組合項的穩定參數與先前的值相等,Compose 就會略過該參數。 - 不穩定參數:啟用嚴格略過模式 (Kotlin 2.0.20 以上版本的預設模式) 後,Compose 會使用例項相等性 (
===) 比較不穩定參數,並在傳遞相同物件例項時略過可組合項。在 Kotlin 2.0.20 之前的版本 (或停用嚴格略過模式時),如果父項重新組成,Compose 一律會重新組成含有不穩定參數的可組合項。
如果應用程式經常為不穩定參數分配新執行個體,或過度註解模型,導致大型集合必須執行耗費資源的 .equals() 檢查,您可能會發現不必要的重組或比較負擔。
本文將詳細說明 Compose 如何判斷穩定性,以及如何最佳化穩定性,進而提升效能和整體使用者體驗。
不可變更的物件
下列程式碼片段說明穩定性和重組背後的一般原則。
Contact 類別是不可變動的資料類別。這是因為其所有參數都是使用 val 關鍵字定義的原型。建立 Contact 的例項後,您就無法變更物件屬性的值。如果嘗試這麼做,就會建立新物件。
data class Contact(val name: String, val number: String)
ContactRow 可組合函式含有 Contact 類型的參數。
@Composable
fun ContactRow(contact: Contact, modifier: Modifier = Modifier) {
var selected by remember { mutableStateOf(false) }
Row(modifier) {
ContactDetails(contact)
ToggleButton(selected, onToggled = { selected = !selected })
}
}
請思考使用者點選切換鈕,且 selected 狀態變更時,會發生什麼情況:
- Compose 會評估是否應重新組成
ContactRow內的程式碼。 - 系統會發現
ContactDetails的唯一引數屬於Contact類型。 - 由於
Contact是不可變動的資料類別,Compose 可確保ContactDetails的引數皆未變更。 - 因此,Compose 會略過
ContactDetails,不會重新組成。 - 另一方面,
ToggleButton的引數已變更,Compose 會重新組合該元件。
可變動的物件
雖然上述範例使用不可變動的物件,但也可以建立可變動的物件。請參考以下程式碼片段:
data class Contact(var name: String, var number: String)
由於 Contact 的每個參數現在都是 var,因此類別不再是不可變類別。如果屬性有所變更,Compose 不會察覺。這是因為 Compose 只會追蹤 Compose 狀態物件的變更。
Compose 會將這類類別視為不穩定。在強略過模式 (Kotlin 2.0.20 以上版本) 中,Compose 會使用執行個體相等性 (===) 比較 Contact:就地變更 contact.name 不會觸發重組,但傳遞具有相同值的新分配 Contact 執行個體,仍會強制 ContactDetails 重組 (如果沒有強略過模式,ContactDetails 會在每次 selected 變更時重組)。
在 Compose 中實作
雖然並非必要,但瞭解 Compose 如何判斷在重組期間要略過哪些函式,可能會有幫助。
Compose 編譯器執行程式碼時,會使用多個標記之一標示每個函式和類型。這些標記反映了 Compose 在重組期間如何處理函式或類型。
函式
Compose 可將函式標示為 skippable 或 restartable。請注意,函式可能標示為其中一項、兩項或兩者皆非:
- 可略過:如果編譯器將可組合項標示為可略過,當所有引數都與先前的值相同時,Compose 可以在重組期間略過該可組合項。(在 Kotlin 2.0.20+ 中,使用嚴格略過模式時,所有可重新啟動的可組合函式都會自動略過)。
- 可重新啟動:可重新啟動的可組合函式可做為重組作業的「範圍」。換句話說,函式可以做為 Compose 的進入點,在狀態變更後開始重新執行程式碼,進行重組。
類型
Compose 會將類型標示為不可變或穩定。每個類型都是其中一種:
- 不可變動:如果屬性值永遠不會變更,且所有方法都具有參照透明性,Compose 會將類型標示為不可變動。
- 請注意,所有原始類型都會標示為不可變。包括
String、Int和Float。
- 請注意,所有原始類型都會標示為不可變。包括
- 穩定:表示屬性在建構後可變更的類型。如果這些屬性在執行階段發生變更,Compose 會偵測到這些變更。
偵錯穩定性
如果應用程式正在重組參數未變更的可組合函式,請先檢查是否每次重組都會分配不穩定類型的新例項 (或者,如果停用強略過模式,請檢查可組合函式是否具有不穩定類型的 var 屬性或 val 屬性)。
如要進一步瞭解如何診斷 Compose 穩定性的複雜問題,請參閱「偵錯穩定性」指南。
修正穩定性問題
如要瞭解如何讓 Compose 實作穩定,請參閱「修正穩定性問題」指南。
摘要
整體而言,請注意下列事項:
- 參數:Compose 會判斷可組合函式各個參數的穩定性,決定在重組期間,要使用結構相等性 (
.equals()) 還是例項相等性 (===) 比較參數。 - 立即修正:如果您發現可組合項未略過,且導致效能問題,請檢查是否每次傳遞都會重新建立不穩定參數的新例項,或是否使用
var屬性而非State。 - 編譯器報告:您可以使用編譯器報告,判斷系統推論出的類別穩定性。
- 集合:Compose 認為標準集合介面 (
List、Set和Map) 不穩定,因為其基礎實作可能不穩定。在強略過模式 (Kotlin 2.0.20 以上版本預設啟用) 下,當集合參照未變更時,不穩定集合仍會使用執行個體相等性 (===) 略過重組。請謹慎考慮是否要將標準集合替換為 Kotlinx 不可變集合,或使用@Immutable或@Stable註解集合保留類別,因為在每次重組時將每個元素與.equals()進行比較,可能會比重組本身更耗費資源。 - 其他模組:Compose 會將未執行 Compose 編譯器的模組類別視為不穩定 (在強化略過模式下使用
===進行比較)。如果資料來源經常從非 Compose 模組重新例項化具有相同值的小型平面模型,您可以設定穩定性設定檔,或在.equals()成本較低時使用@Stable或@Immutable。