中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

開(kāi)發(fā)網(wǎng)頁(yè)系統(tǒng)一般多少錢windows優(yōu)化大師官網(wǎng)

開(kāi)發(fā)網(wǎng)頁(yè)系統(tǒng)一般多少錢,windows優(yōu)化大師官網(wǎng),北京西站出站口,什邡市建設(shè)局門戶網(wǎng)站前言 本文是筆者學(xué)習(xí)Compose是如何自動(dòng)觸發(fā)UI刷新的筆記,可能缺乏一定可讀性和教導(dǎo)性.(建議閱讀參考文獻(xiàn)更具啟發(fā)性) 使用以下BOM作為研究環(huán)境. composeBom "2024.04.01" androidx-compose-bom { group "androidx.compose", name "compose-bom…

前言

本文是筆者學(xué)習(xí)Compose是如何自動(dòng)觸發(fā)UI刷新的筆記,可能缺乏一定可讀性和教導(dǎo)性.(建議閱讀參考文獻(xiàn)更具啟發(fā)性)

使用以下BOM作為研究環(huán)境.

composeBom = "2024.04.01"
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }

我們看一下下面的程序,再點(diǎn)擊OneComposable按鈕的時(shí)候?yàn)槭裁磧H僅TwoComposable重組,其他的Composable不會(huì)?背后是如何實(shí)現(xiàn)特定作用域定向刷新?

@Composable
fun MainCompose3() {Column {Logd("invoke MainCompose3")val displayState = remember { mutableIntStateOf(1) }OneComposable(displayState)TwoComposable(displayState)}
}@Composable
fun TwoComposable(flagState: MutableState<Int>) {Logd("invoke TwoComposable")Text("hello world ${flagState.value}")
}@Composable
fun OneComposable(flagState: MutableState<Int>) {Logd("invoke OneComposable")Button(onClick = {flagState.value = ++flagState.value}) {Text("Change flagState")}}
fun Logd(msg:String){Log.d("test",msg)
}

當(dāng)點(diǎn)擊OneComposable的按鈕重組輸出:

invoke TwoComposable

使用原始View模擬自動(dòng)刷新

我們借用原始 View 系統(tǒng)配合快照完成一個(gè)類似 Compose 自動(dòng)局部刷新.
建議讀者先自行閱讀快照文獻(xiàn):
一文看懂 Jetpack Compose 快照系統(tǒng)
我們布局如下:

在這里插入圖片描述

//MainActivity.kt
class MainActivity : ComponentActivity() {//private val displayOneState = mutableIntStateOf(1)private val displayTwoState = mutableIntStateOf(1)override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,FrameLayout.LayoutParams.MATCH_PARENT)val rootView = FrameLayout(this)setContentView(rootView, layoutParams)//Composer是我們自定義的Composer.setContentView(rootView){//LinearLayoutOneColumnComposable(Composer, rootView) { view ->//Texview 1 read and show displayOneStateOneTvComposable(Composer, view, displayOneState)//Textview 2 => read and show displayTwoStateTwoTvComposable(Composer, view, displayTwoState)//Button => modify displayOneStateOneBtnComposable(Composer, view, displayOneState)}}}
}    

布局展示如下:

在這里插入圖片描述

多次點(diǎn)擊按鈕后Texview 1更新文案

在這里插入圖片描述

我們首先需要了解Composer#setContentView做什么.

//MainActivity.kt
object Composer {fun setContentView(rootView:ViewGroup,content: (ViewGroup) -> Unit){//創(chuàng)建一個(gè)快照,用于感知content對(duì)于 state 的讀取,val snapshot = Snapshot.takeSnapshot(readObserver = { mutableState ->//每次在 content 函數(shù)中任意讀取 state 都會(huì)回調(diào)到此.//content有多個(gè)函數(shù)OneTvComposable,TwoTvComposable都會(huì)讀取不同的state.//我們?nèi)绾螛?biāo)記當(dāng)前state被那個(gè)函數(shù)讀取?})//進(jìn)入快照中enter函數(shù)才可感知state讀寫snapshot.enter {content.invoke(rootView)}}
}

為了在readObserver回調(diào),為感知是那個(gè)函數(shù)讀取,我們?cè)O(shè)計(jì)一個(gè)棧算法,每次調(diào)用xxxxComposable 函數(shù)的時(shí)候構(gòu)建一個(gè)UpdateScope,并壓入棧中.在函數(shù)結(jié)束的時(shí)候彈出棧.
為了方便我們把UpdateScope稱為更新域.

我們首先查看讀取棧的代碼:


val state2Scope = MutableScatterMap<Any, MutableSet<UpdateScope>>()
val scopeStack: Deque<UpdateScope> = java.util.ArrayDeque<UpdateScope>()Snapshot.takeSnapshot(readObserver = { mutableState ->//一個(gè)state 可能會(huì)被多個(gè)UpdateScope讀取var updateScopes = state2Scope[mutableState]if (updateScopes.isNullOrEmpty()) {updateScopes = mutableSetOf()state2Scope[mutableState] = updateScopes}//查看棧頂?shù)膗pdateScopes然后放入一個(gè)映射中.//這樣我們就可以知道 state 更新了哪些 updateScopes 需要被重新重組val updateScope = scopeStack.peek();if (updateScope != null) {updateScopes.add(updateScope)}})
//略

最后我們看看如何構(gòu)造這些updateScopes棧對(duì)象.

//id 標(biāo)記某個(gè)composable函數(shù)方便索引
//update回調(diào)composable在數(shù)據(jù)更新的時(shí)候
data class UpdateScope(val id:Int, val update:()->Unit)
fun OneTvComposable(composer: Composer, parent: ViewGroup, state: MutableIntState) {// 創(chuàng)建scope 然后賦予一個(gè)唯一 id 方便查找.val scope = UpdateScope(0x00001){//數(shù)據(jù)更新的時(shí)候OneTvComposable(composer,parent,state)}scopeStack.push(scope)//創(chuàng)建一個(gè) Textview 去展示val viewId = "OneText"MyText(viewId, "${state.intValue}", parent)scopeStack.pop()
}

其他函數(shù)類似就不演示,我們圖展示下列Composable代碼運(yùn)行流程

OneColumnComposable(Composer, rootView) { view ->OneTvComposable(Composer, view, displayOneState)TwoTvComposable(Composer, view, displayTwoState)OneBtnComposable(Composer, view, displayOneState)
}

OneColumnComposable 函數(shù)內(nèi)部不會(huì)讀取任何狀態(tài),所以僅僅會(huì)壓入棧不會(huì)觸發(fā) snapshot 讀取.圖示例如下:

fun OneColumnComposable(composer: Composer,parent: ViewGroup,content: (ViewGroup) -> Unit
) {// 創(chuàng)建scope 然后賦予一個(gè)唯一 id 方便查找.val scope = UpdateScope(0x00004){//數(shù)據(jù)更新的時(shí)候OneColumnComposable(composer,parent,state)}scopeStack.push(scope)//創(chuàng)建一個(gè) LinearLayoutMyColumn("oneColumn", parent, { view ->content.invoke(view)})scopeStack.pop()}

在這里插入圖片描述

運(yùn)行OneTvComposable,會(huì)壓入一個(gè)新的 Scope,由于在這個(gè)函數(shù)讀取了 state,會(huì)觸發(fā) snapshot 讀取回調(diào),更新updateScope映射信息

在這里插入圖片描述

運(yùn)行TwoTvComposable時(shí),OneTvComposable會(huì)彈出之前的棧.會(huì)壓入一個(gè)新的 Scope,由于在這個(gè)函數(shù)讀取了 state,會(huì)觸發(fā)snapshot 讀取回調(diào),更新updateScope映射信息


fun TwoTvComposable(composer: Composer, parent: ViewGroup, state: MutableIntState) {// 創(chuàng)建scope 然后賦予一個(gè)唯一 id 方便查找.val scope = UpdateScope(0x00002){//數(shù)據(jù)更新的時(shí)候TwoTvComposable(composer,parent,state)}scopeStack.push(scope)//創(chuàng)建一個(gè) Textview 去展示val viewId = "TwoText"MyText(viewId, "${state.intValue}", parent)scopeStack.pop()
}

在這里插入圖片描述

OneBtnComposable函數(shù)并不會(huì)讀取 state 而是簡(jiǎn)單的寫入.所以并不會(huì)影響 state2Scope

fun OneBtnComposable(composer: Composer, parent: ViewGroup, state: MutableIntState) {// 創(chuàng)建scope 然后賦予一個(gè)唯一 id 方便查找.val scope = UpdateScope(0x00003){//數(shù)據(jù)更新的時(shí)候OneBtnComposable(composer,parent,state)}val viewId = "OneBtn"MyButton(viewId, "changeState", parent, {state.intValue += 1})scopeStack.pop()}

在這里插入圖片描述

當(dāng)OneBtnComposable函數(shù)結(jié)束的時(shí)候OneColumnComposable也對(duì)應(yīng)結(jié)束了函數(shù)周期.所有 信息將會(huì)從scopestack將會(huì)彈出

在這里插入圖片描述

現(xiàn)在我們有了state2Scope存儲(chǔ)信息,在 state 更改時(shí)調(diào)用對(duì)應(yīng)的UpdateScope的回調(diào)即可完成更新.

Snapshot.registerGlobalWriteObserver {//全局作用于的快照被寫入的時(shí)候回調(diào)//調(diào)用通知.此時(shí)會(huì)觸發(fā)registerApplyObserver回調(diào)Snapshot.sendApplyNotifications()
}
Snapshot.registerApplyObserver { anies, snapshot ->for (any in anies) {//any 就是我們的 stateval updateScopes = state2Scope[any]//重新調(diào)用函數(shù)觸發(fā)更新updateScopes.update()}
}

上面的設(shè)計(jì)方案有一個(gè)比較致命的性能問(wèn)題比如我們看一下下面的代碼,根布局會(huì)根據(jù)backgroundColorState修改自身背景顏色

private val backgroundColorState = mutableIntStateOf(Color.BLUE)
//OneColumnComposable會(huì)讀取backgroundColorState變量去設(shè)置背景色
OneColumnComposable(Composer, rootView,backgroundColorState) { view ->OneTvComposable(Composer, view, displayOneState)TwoTvComposable(Composer, view, displayTwoState)//按鈕會(huì)修改背景色 OneBtnComposable(Composer, view, backgroundColorState)
}
fun OneColumnComposable(composer: Composer,parent: ViewGroup,content: (ViewGroup) -> Unit
) {// 創(chuàng)建scope 然后賦予一個(gè)唯一 id 方便查找.val scope = UpdateScope(0x00004){//數(shù)據(jù)更新的時(shí)候OneColumnComposable(composer,parent,state)}scopeStack.push(scope)//創(chuàng)建一個(gè) LinearLayout,并制定背景色顏色MyColumn("oneColumn", parent,backgroundColorState.value, { view ->content.invoke(view)})scopeStack.pop()
}

這時(shí)候觸發(fā)切換顏色的時(shí)候我們期待僅有OneColumnComposable會(huì)被回調(diào).但是實(shí)際上OneColumnComposable,OneTvComposable,TwoTvComposable,OneBtnComposable全部會(huì)重新觸發(fā).我們可以在建立一個(gè)新的樹(shù)稱為 Group樹(shù),這個(gè)樹(shù)中每個(gè)節(jié)點(diǎn)存儲(chǔ)是否Dirty,然后更新的時(shí)候選擇性更新判斷.

樹(shù)中節(jié)點(diǎn)如下

data class Group(val id: Int,var parent: Group?,val child: MutableScatterMap<Int, Group> = mutableScatterMapOf()
) {//標(biāo)記節(jié)點(diǎn)是否需要更新var dirtyFlag: Int = DIRTY_STATE_INITcompanion object {val ROOT_NODE = Group(0x0000, null, mutableScatterMapOf())//節(jié)點(diǎn)未重組過(guò),需要重組val DIRTY_STATE_INIT = 0//節(jié)點(diǎn)是干凈的不需要被重組val DIRTY_STATE_CLEAN = DIRTY_STATE_INIT + 1//節(jié)點(diǎn)數(shù)據(jù)過(guò)時(shí)需要重組val DIRTY_STATE_DECAY = DIRTY_STATE_CLEAN + 1}override fun toString(): String {return """ self ${this.id} -> child [${child.joinToString(transform = { ke, v -> v.toString() })}]""".trimMargin()}
}

我們可以在創(chuàng)建scope棧的時(shí)候結(jié)合一起構(gòu)建這個(gè) group 樹(shù).我們舉例OneTvComposable來(lái)說(shuō)明.我們順帶把所有這類任務(wù)的代碼放入一個(gè)叫Composer對(duì)象中

fun OneTvComposable(composer: Composer, parent: ViewGroup, state: MutableIntState) {//創(chuàng)建一個(gè) scope 棧對(duì)象,并且創(chuàng)建一個(gè) group 樹(shù)節(jié)點(diǎn)val group = composer.startGroup(0x00001)if (group.change()) {Logd("invoke OneTvComposable")val viewId = "OneText"MyText(viewId, "${state.intValue}", parent)} else {}//結(jié)束的時(shí)候時(shí)候我們我們彈出 scope 棧對(duì)象,并維護(hù) group 樹(shù)節(jié)點(diǎn)group.endScope {OneTvComposable(composer, parent, state)}
}//Composer對(duì)象內(nèi)嵌函數(shù)
class Composer{//標(biāo)記由于 state 寫入需要重新刷新的 groupval dirtyGroup: MutableScatterMap<Int, Group> = mutableScatterMapOf<Int, Group>()fun startGroup(composableId: Int): UpdateScope {//調(diào)用startGroup此 Group已經(jīng)被重組,移除標(biāo)記val dirtyGroup = dirtyGroup.remove(composableId)//構(gòu)建好 group 樹(shù)節(jié)點(diǎn),這個(gè)樹(shù)用于判斷數(shù)據(jù)變化時(shí)更新策略.提升重組性能val group = if (dirtyGroup == null) {val parentGroup =scopeStack.peek()?.group ?: rootNodeval group = dirtyGroup ?: parentGroup.child[composableId] ?: Group(composableId,scopeStack.peek()?.group,mutableScatterMapOf())parentGroup.child[composableId] = groupgroup} else {dirtyGroup}//構(gòu)造 scope 棧對(duì)象,方便感知刷新域val updateScope = UpdateScope(composableId, group, null)scopeStack.push(updateScope)return updateScope}//彈出棧,并重新標(biāo)記 group 為干凈fun endScope(update: (() -> Unit)) {this.update = updateComposer.scopeStack.pop()group.dirtyFlag = DIRTY_STATE_CLEAN}
}

最后我們?cè)诓殚喯聦懭牖卣{(diào)處的處理.

Snapshot.registerGlobalWriteObserver {Snapshot.sendApplyNotifications()
}
Snapshot.registerApplyObserver { anies, snapshot ->for (any in anies) {val updateScopes = state2Scope[any]updateScopes?.forEach { scope ->dirtyGroup[scope.id] = (scope.group)//僅標(biāo)記被污染的 group,可以避免子group也過(guò)度參與.scope.group.dirtyFlag = DIRTY_STATE_DECAYupdateFrame(scope)}}
}
//開(kāi)始重組
private fun updateFrame(updateScope: UpdateScope) {while (scopeStack.isNotEmpty()) {val popScope = scopeStack.pop()if (updateScope == popScope) {break}}updateScope.update?.invoke()
}

上面便是一個(gè)簡(jiǎn)易版本 View 下模擬 compose 流程.Group樹(shù)用數(shù)據(jù)變時(shí)怎么樣刷新,UpdateSope用于在哪刷新,而Composable描述了怎么樣的一個(gè) View 樹(shù)

在這里插入圖片描述

最后我們貼出完整相關(guān)代碼

data class Group(val id: Int,var parent: Group?,val child: MutableScatterMap<Int, Group> = mutableScatterMapOf()
) {var dirtyFlag: Int = DIRTY_STATE_INITcompanion object {val ROOT_NODE = Group(0x0000, null, mutableScatterMapOf())val DIRTY_STATE_INIT = 0val DIRTY_STATE_CLEAN = DIRTY_STATE_INIT + 1val DIRTY_STATE_DECAY = DIRTY_STATE_CLEAN + 1}override fun toString(): String {return """ self ${this.id} -> child [${child.joinToString(transform = { ke, v -> v.toString() })}]""".trimMargin()}
}class UpdateScope(val id: Int, val group: Group, var update: (() -> Unit)? = null) {override fun equals(other: Any?): Boolean {if (other !is UpdateScope) {return false}return other.id == this.id}override fun hashCode(): Int {return this.id}fun endScope(update: (() -> Unit)) {this.update = updateComposer.scopeStack.pop()group.dirtyFlag = DIRTY_STATE_CLEAN}fun change(): Boolean {return group.dirtyFlag == DIRTY_STATE_DECAY || group.dirtyFlag == DIRTY_STATE_INIT}
}object Composer {val state2Scope = MutableScatterMap<Any, MutableSet<UpdateScope>>()val scopeStack: Deque<UpdateScope> = java.util.ArrayDeque<UpdateScope>()val dirtyGroup: MutableScatterMap<Int, Group> = mutableScatterMapOf<Int, Group>()val rootNode: Group = ROOT_NODEinit {Snapshot.registerGlobalWriteObserver {Snapshot.sendApplyNotifications()}Snapshot.registerApplyObserver { anies, snapshot ->for (any in anies) {val updateScopes = state2Scope[any]updateScopes?.forEach { scope ->dirtyGroup[scope.id] = (scope.group)scope.group.dirtyFlag = DIRTY_STATE_DECAYupdateFrame(scope)}}}}private fun updateFrame(updateScope: UpdateScope) {while (scopeStack.isNotEmpty()) {val popScope = scopeStack.pop()if (updateScope == popScope) {break}}updateScope.update?.invoke()}fun startGroup(composableId: Int): UpdateScope {val dirtyGroup = dirtyGroup.remove(composableId)val group = if (dirtyGroup == null) {val parentGroup =scopeStack.peek()?.group ?: rootNodeval group = dirtyGroup ?: parentGroup.child[composableId] ?: Group(composableId,scopeStack.peek()?.group,mutableScatterMapOf())parentGroup.child[composableId] = groupgroup} else {dirtyGroup}val updateScope = UpdateScope(composableId, group, null)scopeStack.push(updateScope)return updateScope}fun setContentView(rootView: ViewGroup, content: (ViewGroup) -> Unit) {val snapshot = Snapshot.takeSnapshot(readObserver = { mutableState ->var updateScopes = state2Scope[mutableState]if (updateScopes.isNullOrEmpty()) {updateScopes = mutableSetOf()state2Scope[mutableState] = updateScopes}val updateScope = scopeStack.peek();if (updateScope != null) {updateScopes.add(updateScope)}})snapshot.enter {content.invoke(rootView)}}
}class MainActivity : ComponentActivity() {private val displayOneState = mutableIntStateOf(1)private val displayTwoState = mutableIntStateOf(1)private val backgroundColorState = mutableIntStateOf(android.graphics.Color.BLUE)override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,FrameLayout.LayoutParams.MATCH_PARENT)val rootView = FrameLayout(this)setContentView(rootView, layoutParams)Composer.setContentView(rootView) {OneColumnComposable(Composer, rootView, backgroundColorState) { view ->OneTvComposable(Composer, view, displayOneState)TwoTvComposable(Composer, view, displayTwoState)OneBtnComposable(Composer, view, backgroundColorState)}}Log.d("fmy", "tree : ${Composer.rootNode}")}fun OneColumnComposable(composer: Composer,parent: ViewGroup,backgroundColorState: MutableIntState,content: (ViewGroup) -> Unit) {val group = composer.startGroup(0x00004)if (group.change()) {Logd("invoke OneColumnComposable")MyColumn("oneColumn", parent, backgroundColorState.intValue) { view ->content.invoke(view)}} else {}group.endScope {OneColumnComposable(composer, parent, this.backgroundColorState, content)}}fun MyColumn(viewId: String,parent: ViewGroup,backgroundColor: Int,content: (ViewGroup) -> Unit) {val llView = parent.findViewWithTag<LinearLayout>(viewId) ?: LinearLayout(this)if (llView.parent == null) {llView.tag = viewIdval layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT)parent.addView(llView, layoutParams)llView.orientation = LinearLayout.VERTICAL}
//        llView.setBackgroundResource(R.color.teal_200)llView.setBackgroundColor(backgroundColor)content.invoke(llView)}fun MyText(viewId: String, content: String, parent: ViewGroup) {val oldText = parent.findViewWithTag<TextView>(viewId)val textView = if (oldText == null) {val textView = TextView(this)textView.tag = viewIdparent.addView(textView)textView} else {oldText}textView.text = content}fun MyButton(viewId: String, content: String, parent: ViewGroup, click: () -> Unit) {val oldBtn = parent.findViewWithTag<Button>(viewId)val btn = if (oldBtn == null) {val btn = Button(this)btn.tag = viewIdparent.addView(btn)btn} else {oldBtn}btn.text = contentbtn.setOnClickListener { click.invoke() }}fun OneTvComposable(composer: Composer, parent: ViewGroup, state: MutableIntState) {//創(chuàng)建一個(gè) scope 棧對(duì)象,并且創(chuàng)建一個(gè) group 樹(shù)節(jié)點(diǎn)val group = composer.startGroup(0x00001)if (group.change()) {Logd("invoke OneTvComposable")val viewId = "OneText"MyText(viewId, "${state.intValue}", parent)} else {}//結(jié)束的時(shí)候時(shí)候我們我們彈出 scope 棧對(duì)象,并維護(hù) group 樹(shù)節(jié)點(diǎn)group.endScope {OneTvComposable(composer, parent, state)}}fun TwoTvComposable(composer: Composer, parent: ViewGroup, state: MutableIntState) {val group = composer.startGroup(0x00002)if (group.change()) {val viewId = "TwoText"Logd("invoke TwoTvComposable")MyText(viewId, "${state.intValue}", parent)} else {}group.endScope {TwoTvComposable(composer, parent, state)}}fun OneBtnComposable(composer: Composer, parent: ViewGroup, state: MutableIntState) {val group = composer.startGroup(0x00003)if (group.change()) {val id = "OneBtn"Logd("invoke OneBtnComposable")MyButton(id, "changeState", parent, {
//                state.intValue += 1state.intValue = Color.RED})} else {}group.endScope {OneBtnComposable(composer, parent, state)}}}

Compose 源碼閱讀

我們有如下Demo作為講解說(shuō)明.
一個(gè)按鈕和一個(gè)文本,每次點(diǎn)擊按鈕觸發(fā)數(shù)字單調(diào)遞增
在這里插入圖片描述

示例代碼如下:

//MainActivity.kt
class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {Home()}}
}
@Composable
private fun Home() {ComposeDemoTheme {Surface {Column {val displayState = remember { mutableIntStateOf(1) }MainCompose(displayState)Button(onClick = {displayState.intValue = ++displayState.intValue}) {Text("increase displayState")}}}}
}
@Composable
fun MainCompose(displayState: MutableState<Int>) {val value = displayState.valueText("display $value")
}@Composable
@Preview
fun HomePreview(){Home()
}

本文需要有基礎(chǔ)的快照SlotTable概念以避免重復(fù)造輪子.

手?jǐn)] View 下局部自動(dòng)更新

MainCompose原始的函數(shù)會(huì)在編譯后變?yōu)橐韵麓a.

   @Composable@ComposableTarget(applier = "androidx.compose.ui.UiComposable")public static final void MainCompose(@NotNull final MutableState displayState, @Nullable Composer $composer, final int $changed) {//每一個(gè) compose 都會(huì)構(gòu)建一個(gè) Group,最終Group也會(huì)組成一個(gè)樹(shù).(一定要注意這個(gè)不是渲染樹(shù) LayoutNode,Compose 里有多顆樹(shù),這顆樹(shù)用做數(shù)據(jù)處理) //而startRestartGroup也會(huì)創(chuàng)建一個(gè) Group 放入樹(shù)中$composer = $composer.startRestartGroup(-1327587884);ComposerKt.sourceInformation($composer, "C(MainCompose)47@1341L22:MainActivity.kt#ffoge4");//結(jié)合一些數(shù)據(jù)判斷當(dāng)前是否可以跳過(guò)重組int $dirty = $changed;if (($changed & 14) == 0) {$dirty |= $composer.changed(displayState) ? 4 : 2;}//如果當(dāng)前 Composeable是skippable那么會(huì)結(jié)合當(dāng)前入?yún)⑴袛嗍欠衲芴^(guò)//skippable本文后面會(huì)簡(jiǎn)單介紹if (($dirty & 11) == 2 && $composer.getSkipping()) {$composer.skipToGroupEnd();} else {if (ComposerKt.isTraceInProgress()) {ComposerKt.traceEventStart(-1327587884, $dirty, -1, "com.example.composedemo.MainCompose (MainActivity.kt:45)");}//如果需要重組那么進(jìn)行int value = ((Number)displayState.getValue()).intValue();TextKt.Text--4IGK_g("display " + value, (Modifier)null, 0L, 0L, (FontStyle)null, (FontWeight)null, (FontFamily)null, 0L, (TextDecoration)null, (TextAlign)null, 0L, 0, false, 0, 0, (Function1)null, (TextStyle)null, $composer, 0, 0, 131070);if (ComposerKt.isTraceInProgress()) {ComposerKt.traceEventEnd();}}//標(biāo)記當(dāng)前 Group 在樹(shù)中結(jié)束,并返回一個(gè) Compose 更新域(ScopeUpdateScope).//ScopeUpdateScope會(huì)在displayState更新時(shí)調(diào)用updateScope進(jìn)而發(fā)生重組ScopeUpdateScope var5 = $composer.endRestartGroup();if (var5 != null) {var5.updateScope((Function2)(new Function2() {public final void invoke(Composer $composer, int $force) {//如果數(shù)據(jù)變更會(huì)會(huì)回調(diào)MainActivityKt.MainCompose(displayState, $composer, RecomposeScopeImplKt.updateChangedFlags($changed | 1));}// $FF: synthetic method// $FF: bridge methodpublic Object invoke(Object p1, Object p2) {this.invoke((Composer)p1, ((Number)p2).intValue());return Unit.INSTANCE;}}));}}

你會(huì)驚訝的發(fā)現(xiàn)函數(shù)背后做的事情和我們自己實(shí)現(xiàn)在 View 下差不多.
我們這里額外補(bǔ)充一個(gè)細(xì)節(jié),你會(huì)注意到有一個(gè)$composer.getSkipping()函數(shù)才會(huì)判斷當(dāng)前 Composeable 是否會(huì)跳過(guò),否則一定會(huì)觸發(fā)重組.

那么時(shí)候函數(shù)getSkipping才為 true 呢?
Compose 編譯器會(huì)為每個(gè)Composable做一個(gè)標(biāo)記.如果利用可以利用入?yún)⒑椭皞魅雲(yún)?shù)判斷相等那么可以被標(biāo)記skippable.

我們比較下下面的兩個(gè)函數(shù)是否都可以被標(biāo)記skippable?

//可以被標(biāo)記skippable,因?yàn)閐isplayState數(shù)值可以取出來(lái)和之前的比較
@Composable
fun MainCompose(displayState: MutableState<Int>) {val value = displayState.valueText("display $value")
}//不可以被標(biāo)記skippable,因?yàn)閘ist的實(shí)例可以比較,但是內(nèi)部的內(nèi)容和順序不可推斷
@Composable
fun MainCompose2(list: MutableList<String>) {Text("display $${list.joinToString { it }}")
}

相關(guān)具體知識(shí)點(diǎn)建議閱讀
what-do-the-stable-and-immutable-annotations-mean-in-jetpack-compose

有相關(guān)工具可以打印出編譯視角下函數(shù)結(jié)構(gòu),這里直接給出結(jié)果:

//標(biāo)記skippable
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun MainCompose(stable displayState: MutableState<Int>
)
//不標(biāo)記skippable,這個(gè)函數(shù)被重組的時(shí)候一定會(huì)重新觸發(fā).
restartable scheme("[androidx.compose.ui.UiComposable]") fun MainCompose2(unstable list: MutableList<String>
)

我們看下 MainCompose2被編譯后的代碼是不會(huì)存在skipToGroupEnd函數(shù)的調(diào)用.重組時(shí)直接觸發(fā)不存在跳過(guò)邏輯.

@Composable
fun MainCompose2(list: MutableList<String>) {Text("display $${list.joinToString { it }}")
}@Composable@ComposableTarget(applier = "androidx.compose.ui.UiComposable")public static final void MainCompose2(@NotNull final List list, @Nullable Composer $composer, final int $changed) {$composer = $composer.startRestartGroup(1711764239);ComposerKt.sourceInformation($composer, "C(MainCompose2)51@1428L44:MainActivity.kt#ffoge4");if (ComposerKt.isTraceInProgress()) {ComposerKt.traceEventStart(1711764239, $changed, -1, "com.example.composedemo.MainCompose2 (MainActivity.kt:50)");}TextKt.Text--4IGK_g("display $" + CollectionsKt.joinToString$default((Iterable)list, (CharSequence)null, (CharSequence)null, (CharSequence)null, 0, (CharSequence)null, (Function1)null.INSTANCE, 31, (Object)null), (Modifier)null, 0L, 0L, (FontStyle)null, (FontWeight)null, (FontFamily)null, 0L, (TextDecoration)null, (TextAlign)null, 0L, 0, false, 0, 0, (Function1)null, (TextStyle)null, $composer, 0, 0, 131070);if (ComposerKt.isTraceInProgress()) {ComposerKt.traceEventEnd();}ScopeUpdateScope var3 = $composer.endRestartGroup();if (var3 != null) {var3.updateScope((Function2)(new Function2() {public final void invoke(Composer $composer, int $force) {MainActivityKt.MainCompose2(list, $composer, RecomposeScopeImplKt.updateChangedFlags($changed | 1));}// $FF: synthetic method// $FF: bridge methodpublic Object invoke(Object p1, Object p2) {this.invoke((Composer)p1, ((Number)p2).intValue());return Unit.INSTANCE;}}));}}

我們 Compose 下的startRestartGroup是如何實(shí)現(xiàn),

//Composer.ktclass ComposerImpl(@ComposeCompilerApioverride fun startRestartGroup(key: Int): Composer {//創(chuàng)造一個(gè) Group 樹(shù)節(jié)點(diǎn),由于這塊比較復(fù)雜不展開(kāi)細(xì)說(shuō)start(key, null, GroupKind.Group, null)//創(chuàng)建一個(gè)重組域addRecomposeScope()return this}//創(chuàng)建一個(gè)重組域放入棧中private fun addRecomposeScope() {//...略val scope = RecomposeScopeImpl(composition as CompositionImpl)invalidateStack.push(scope)//...略}@ComposeCompilerApioverride fun endRestartGroup(): ScopeUpdateScope? {//...略//彈出棧val scope = if (invalidateStack.isNotEmpty()) invalidateStack.pop()//...略}}

我們最后官方源碼中,入口快照 take函數(shù)調(diào)用處如下所示

 //ReComposer.ktclass Recomposer{private inline fun <T> composing(composition: ControlledComposition,modifiedValues: IdentityArraySet<Any>?,block: () -> T): T {val snapshot = Snapshot.takeMutableSnapshot(readObserverOf(composition), writeObserverOf(composition, modifiedValues))try {return snapshot.enter(block)} finally {applyAndCheck(snapshot)}}
}

我們首先看readObserverOf實(shí)現(xiàn)

//Composition.kt
//以 state 為 key,RecomposeScopeImpl為 value
//value內(nèi)部還有一層List封裝,因?yàn)?state 可以映射多個(gè)RecomposeScopeImpl
private val observations = ScopeMap<RecomposeScopeImpl>()override fun recordReadOf(value: Any) {//value 就是 state 對(duì)象//currentRecomposeScope就是更新域composer.currentRecomposeScope?.let {//存儲(chǔ)state 和RecomposeScopeImpl關(guān)系observations.add(value, it)}
}internal val currentRecomposeScope: RecomposeScopeImpl?
//查閱棧頂 scopeget() = invalidateStack.let {if (childrenComposing == 0 && it.isNotEmpty()) it.peek() else null}

封裝的ScopeMap如下:

package androidx.compose.runtime.collectioninternal class ScopeMap<T : Any> {val map = mutableScatterMapOf<Any, Any>()val size get() = map.size//內(nèi)部會(huì)構(gòu)建 Set 集合放入多個(gè) value 去對(duì)應(yīng)一個(gè) keyfun add(key: Any, scope: T) {map.compute(key) { _, value ->when (value) {null -> scopeis MutableScatterSet<*> -> {@Suppress("UNCHECKED_CAST")(value as MutableScatterSet<T>).add(scope)value}else -> {if (value !== scope) {val set = MutableScatterSet<T>()@Suppress("UNCHECKED_CAST")set.add(value as T)set.add(scope)set} else {value}}}}}
}

我們知道快照有兩個(gè)作用域一個(gè)全局的和 snapshot.enter后綁定的. 而我們業(yè)務(wù)中往往在全局作用域去寫入state,所以本文我們先不閱讀writeObserverOf代碼.(如果對(duì)快照概念模糊建議閱讀參考文獻(xiàn))
Compose全局寫入觀察位于如下代碼中:

//Recomposer.kt
private suspend fun recompositionRunner(block: suspend CoroutineScope.(parentFrameClock: MonotonicFrameClock) -> Unit
) {//...Snapshot.registerApplyObserver { changed, _ ->//這里 Lamba最后的deriveStateLocked會(huì)返回一個(gè)協(xié)程的Continuation//Continuation.resume 調(diào)用會(huì)恢對(duì)應(yīng)協(xié)程繼續(xù)運(yùn)行synchronized(stateLock) {if (_state.value >= State.Idle) {changed.fastForEach {//it 是 state 對(duì)象//將所有被修改 state 放入集合中snapshotInvalidations.add(it)}//最后通知某個(gè)協(xié)程函數(shù),去觸發(fā)重組deriveStateLocked()} else null}?.resume(Unit)}//...
}private var workContinuation: CancellableContinuation<Unit>? = null
private fun deriveStateLocked(): CancellableContinuation<Unit>? {return if (newState == State.PendingWork) {//這里高階函數(shù)的作用是先workContinuation返回,再將workContinuation設(shè)置為 nullworkContinuation.also {workContinuation = null}} else null
}

我們通過(guò)上面的分析workContinuation賦值點(diǎn)就是就是Compose開(kāi)始重組核心點(diǎn)

//Composer.kt
suspend fun runRecomposeAndApplyChanges() = recompositionRunner { parentFrameClock ->//..略//為簡(jiǎn)化流程shouldKeepRecomposing可以視為永遠(yuǎn)為 truewhile (shouldKeepRecomposing) {//判斷當(dāng)前是否dirty 的 scope,如果沒(méi)有那么將當(dāng)前協(xié)程掛起,并將continuation 賦值給workContinuation//可以簡(jiǎn)單判斷snapshotInvalidations為空就執(zhí)行掛起awaitWorkAvailable()//等候下一個(gè) VSYNC 回調(diào)執(zhí)行實(shí)際重組.parentFrameClock.withFrameNanos { frameTime ->//這里會(huì)取出 dirty 的scope 開(kāi)始進(jìn)行重組工作//...略//toRecompose是一個(gè)CompositionImpl集合.//Mainwhile (toRecompose.isNotEmpty() || toInsert.isNotEmpty()) {try {toRecompose.fastForEach { composition ->alreadyComposed.add(composition)//最終會(huì)取出對(duì)應(yīng) scope回調(diào) 遞歸回調(diào)函數(shù)performRecompose(composition, modifiedValues)?.let {toApply += it}}} catch (e: Exception) {processCompositionError(e, recoverable = true)clearRecompositionState()return@withFrameNanos} finally {toRecompose.clear()}}}}

我們最后看看awaitWorkAvailable相關(guān)代碼

//Recomposer.ktprivate val hasSchedulingWork: Booleanget() = synchronized(stateLock) {//是否有 dirty 的 scopesnapshotInvalidations.isNotEmpty() ||compositionInvalidations.isNotEmpty() ||hasBroadcastFrameClockAwaitersLocked}private suspend fun awaitWorkAvailable() {if (!hasSchedulingWork) {suspendCancellableCoroutine<Unit> { co ->synchronized(stateLock) {//如果有 dirty 的數(shù)據(jù)那么直接恢復(fù)協(xié)程完成重組if (hasSchedulingWork) {co} else {//掛起協(xié)程workContinuation = conull}}?.resume(Unit)}}
}

參考

一文看懂 Jetpack Compose 快照系統(tǒng)
探索 Jetpack Compose 內(nèi)核:深入 SlotTable 系統(tǒng)
what-do-the-stable-and-immutable-annotations-mean-in-jetpack-compose

http://www.risenshineclean.com/news/58417.html

相關(guān)文章:

  • 中學(xué)生旅游網(wǎng)站開(kāi)發(fā)的論文怎么寫外鏈seo推廣
  • 都昌網(wǎng)站建設(shè)seo可以從哪些方面優(yōu)化
  • 企業(yè)網(wǎng)站推廣設(shè)計(jì)網(wǎng)站優(yōu)化推廣
  • 個(gè)人網(wǎng)站怎么做支付寶接口google推廣妙招
  • wordpress主題生成seo這個(gè)行業(yè)怎么樣
  • 做爰網(wǎng)站美女東莞今日頭條最新消息
  • 網(wǎng)站建設(shè)怎么樣真正免費(fèi)建站
  • 網(wǎng)站做推廣需要多少錢seo博客優(yōu)化
  • 網(wǎng)站建設(shè)畢業(yè)設(shè)計(jì)中期檢查自己怎么創(chuàng)建一個(gè)網(wǎng)站
  • 重慶網(wǎng)站建設(shè)公司模板地推掃碼平臺(tái)
  • 找電商平臺(tái)幫我賣產(chǎn)品網(wǎng)站優(yōu)化排名工具
  • 鷹潭市城鄉(xiāng)建設(shè)局網(wǎng)站seo實(shí)戰(zhàn)密碼第四版
  • ASP動(dòng)態(tài)商業(yè)網(wǎng)站建設(shè)案例山西seo優(yōu)化公司
  • 用織夢(mèng)做的網(wǎng)站一般后臺(tái)個(gè)人主頁(yè)網(wǎng)頁(yè)設(shè)計(jì)
  • 網(wǎng)站如何做友情鏈接edm營(yíng)銷
  • 公司網(wǎng)站鏈接開(kāi)封網(wǎng)站優(yōu)化公司
  • 筋鄭州做網(wǎng)站百度云搜索引擎入口網(wǎng)盤搜索神器
  • 代碼生成器app下載天津百度關(guān)鍵詞seo
  • 公司oa辦公平臺(tái)寧波seo優(yōu)化服務(wù)
  • 做產(chǎn)品表情的網(wǎng)站手機(jī)上怎么制作網(wǎng)頁(yè)
  • 上海美術(shù)設(shè)計(jì)公司廣州百度提升優(yōu)化
  • 智能手機(jī)網(wǎng)站模板四平網(wǎng)絡(luò)推廣
  • WordPress旋轉(zhuǎn)圖標(biāo)seo模板建站
  • 做ppt介紹網(wǎng)站嗎抖音關(guān)鍵詞排名軟件
  • 主流建站cms品牌seo培訓(xùn)咨詢
  • 大唐工作室 網(wǎng)站制作天貓seo搜索優(yōu)化
  • 做游戲網(wǎng)站教程百度seo是啥意思
  • 手機(jī)網(wǎng)站解析域名百度一下你就知道了
  • 網(wǎng)站推廣辦法百度seo搜搜
  • 網(wǎng)站制作大型公司西安網(wǎng)絡(luò)推廣公司網(wǎng)絡(luò)推廣