天天看點

Jetpack Compose 的階段

前言

Compose 會通過幾個不同的“階段”來渲染幀。如果我們觀察一下 Android View 系統,就會發現它有 3 個主要階段:測量、布局和繪制。Compose 和它非常相似,但開頭多了一個叫做“組合”的重要階段。

幀的三個階段

組合(Composition)

布局(測量和放置)(Layout)

繪制 (Drawing)

Jetpack Compose 的階段

View 的三個階段

measure、layout、draw

compose 關鍵詞

幂等:輸入計算出相同結果的工作

單向資料流:資料能夠沿一個方向(從組合到布局,再到繪制)生成幀. 【有特例】

智能重組: Compose 會跟蹤不同階段中的狀态讀取

狀态讀取

狀态的建立:實作state 接口的類 例如:mutableStateOf() ,而 LiveData,RxJava,Flow 可以通過相容api進行轉換

讀取:直接通路 value 屬性,或使用 Kotlin 屬性委托(值引用時與value讀取方式等價)

重新開機作用域:每個可以在讀取狀态發生更改時重新執行的代碼塊

// State read without property delegate.
val paddingState: MutableState<Dp> = remember { mutableStateOf(8.dp) }
Text(
    text = "Hello",
    modifier = Modifier.padding(paddingState.value)
)      
// State read with property delegate.
var padding: Dp by remember { mutableStateOf(8.dp) }
Text(
    text = "Hello",
    modifier = Modifier.padding(padding)
)      
分階段狀态讀取

第 1 階段:組合

@Composable 函數或 lambda 代碼塊中的狀态讀取會影響組合階段,并且可能會影響後續階段。

當狀态值發生更改時,Recomposer 會安排重新運作所有要讀取相應狀态值的可組合函數,如果輸入未更改,則跳過

第 2 階段:布局

布局階段包含兩個步驟:測量和放置。測量步驟會運作傳遞給 Layout 可組合項的測量 lambda,

放置步驟會運作 layout 函數的放置位置塊、Modifier.offset { … } 的 lambda 塊,等等。

@Compose
fun Layout(measure:(*)->Unit){
   measure.invoke(*)
}

interface LayoutModifier{
   MeasureScope.measure
}      

更确切地說,測量步驟和放置步驟分别具有單獨的重新開機作用域.放置步驟中的狀态讀取不會在此之前重新調用測量步驟。

不過,這兩個步驟通常是交織在一起的,是以在放置步驟中讀取的狀态可能會影響屬于測量步驟的其他重新開機作用域。

第 3 階段:繪制

繪制代碼期間的狀态讀取會影響繪制階段。常見示例包括 Canvas()、Modifier.drawBehind 和 Modifier.drawWithContent。

當狀态值發生更改時,Compose 界面隻會運作繪制階段。

Jetpack Compose 的階段
優化狀态讀取
Box {
    val listState = rememberLazyListState()

    Image(
        // Non-optimal implementation!
        Modifier.offset(
            with(LocalDensity.current) {
                // State read of firstVisibleItemScrollOffset in composition
                (listState.firstVisibleItemScrollOffset / 2).toDp()
            }
 
        )
    )

    LazyColumn(state = listState)
}      

總體思路是正确的:嘗試将狀态讀取定位到盡可能靠後的階段,進而盡可能降低 Compose 需要執行的工作量。

重組循環(循環階段依賴項)