Android開發(fā)Compose remember原理解析
正文
看過(guò)Compose案例或者源碼的你,相信肯定是見過(guò) remember
了的。顧名思義,Compose是要讓我們的代碼“記住”東西,那到底是記住什么呢?要是不 remember
,相關(guān)功能就實(shí)現(xiàn)不了了嗎?
帶著這些問題,來(lái)一探究竟吧
隨機(jī)色文本
假設(shè)有這么一個(gè)“隨機(jī)底色文本”的需求:實(shí)現(xiàn)一個(gè) Text,其背景色每次啟動(dòng)都隨機(jī)產(chǎn)生,且生命周期內(nèi)不變
用Compose可以實(shí)現(xiàn)如下:
private val items = arrayOf(Color.Red, Color.Gray, Color.Magenta, Color.Blue, Color.Green, Color.Cyan) @Composable fun ColorText(name: String) { val color = items.random() val clicked = mutableStateOf(0) Log.d("ct", "ui compose") Column { Text( text = "I'm colored: ${clicked.value}", modifier = Modifier .padding(16.dp) .background(color) .clickable { Log.d("ct", "clicked") clicked.value = clicked.value + 1 } ) } }
Text
調(diào)用 Modifier.background
,設(shè)置隨機(jī)從items中取的顏色,每次新的啟動(dòng),都可能不一樣
然而很遺憾,上面的背景色雖然是隨機(jī)產(chǎn)生,但是單次生命周期里,就可能發(fā)生變化 —— 比如點(diǎn)擊一下文本,如下圖:
更奇怪的是,改變的 clicked 值,并沒有如預(yù)期一樣生效,一直是0……
現(xiàn)象和代碼不一?
原因分析
首先,上述代碼中的“點(diǎn)擊計(jì)數(shù)” clicked,僅僅是為了測(cè)試而存在。因?yàn)樗且粋€(gè) MutableState
對(duì)象,點(diǎn)擊后改變其值,會(huì)觸發(fā)Recomposition流程,于是組件刷新。這樣一來(lái),color 的值將重新由隨機(jī)函數(shù)算出,我們就看到背景色在變化了
同理,雖然我們好像在點(diǎn)擊的時(shí)候改變了 clicked 的值,希望像view系統(tǒng)一樣,界面直接響應(yīng)響應(yīng)。但是,因?yàn)镽ecomposition的存在,它又被重新構(gòu)造了,所以其值還是0
正確實(shí)現(xiàn)
要實(shí)現(xiàn)背景色的整個(gè)生命周期固定,但點(diǎn)擊文本后,點(diǎn)擊計(jì)數(shù)要更新,應(yīng)該這么做:
@Composable fun ColorText(name: String) { val color = remember { items.random() } val clicked = remember { mutableStateOf(0) } //... }
僅僅將color和clicked由 remember
包裹起來(lái)就解決了問題
remember的原理剖析
前面功能的實(shí)現(xiàn),全仗著remember
的加持。它究竟是個(gè)啥?
我們先從顏色的remember著手,它調(diào)用的是這個(gè):
@Composable inline fun <T> remember(calculation: @DisallowComposableCalls () -> T): T = currentComposer.cache(false, calculation)
其注釋寫明了兩個(gè)關(guān)鍵點(diǎn),這也是它的功能描述:
- 記憶由
calculation
返回的值,僅在composition中執(zhí)行 - 在Recomposition過(guò)程中,不會(huì)重新計(jì)算,而是直接返回第1步的值
那么這又是怎么做到的呢?
進(jìn)一步的相關(guān)代碼:
@ComposeCompilerApi inline fun <T> Composer.cache(invalid: Boolean, block: () -> T): T { @Suppress("UNCHECKED_CAST") return rememberedValue().let { // 無(wú)效或Empty值時(shí),走if流程,計(jì)算并保存值,否則直接返回 // remember傳入的invalid為false,所以肯定走值判斷 if (invalid || it === Composer.Empty) { val value = block() updateRememberedValue(value) value } else it } as T } // 要么返回Composer.Empty ,要么返回傳給updateRememberedValue的值 @ComposeCompilerApi fun rememberedValue(): Any? // 更新調(diào)用rememberedValue()后的值,且此值在下一次調(diào)用rememberedValue()時(shí)返回 @ComposeCompilerApi fun updateRememberedValue(value: Any?) interface Composer { // .... companion object { /** * 用于標(biāo)記無(wú)值的狀態(tài) */ val Empty = object { override fun toString() = "Empty" } } }
從上述代碼注釋中,基本上已經(jīng)對(duì)原理很清楚了,簡(jiǎn)單地說(shuō)就是:
- 由composer作為存儲(chǔ)控制
- 無(wú)值時(shí),走初始化邏輯并返回值,同時(shí)存儲(chǔ)該值;有值時(shí),直接返回已存儲(chǔ)的值
顏色的“值不變”清楚了,那點(diǎn)擊計(jì)數(shù)的“值變”又是怎么回事呢?
其實(shí)如出一轍,只是點(diǎn)擊計(jì)數(shù)remember的,不是普通值,而是一個(gè) MutableState
類型。這樣一來(lái),它就有兩層含義了:
- MutableState對(duì)象本身在整個(gè)composition生命周期不變 —— 即類似普通值的狀態(tài)一致性
- MutableState對(duì)象所存儲(chǔ)的實(shí)際值,可變 —— 這用以觸發(fā)Recomposition,并且獲取更新值
小結(jié)
remember
的存在,其實(shí)就是 Compose 機(jī)制下的產(chǎn)物,用以解決recomposition時(shí)的值恢復(fù)問題。而因?yàn)樗?ldquo;值不變”特性,還可以用來(lái)解決耗時(shí)計(jì)算的問題,即,耗時(shí)計(jì)算被remember了,那它就只會(huì)執(zhí)行一次,避免了不必要的額外開銷
以上就是Android開發(fā)Compose remember原理解析的詳細(xì)內(nèi)容,更多關(guān)于Android開發(fā)Compose remember的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android使用Handler實(shí)現(xiàn)倒計(jì)時(shí)功能
這篇文章主要為大家詳細(xì)介紹了Android使用Handler實(shí)現(xiàn)倒計(jì)時(shí)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06Android ListView和Adapter數(shù)據(jù)適配器的簡(jiǎn)單介紹
這篇文章主要介紹了Android ListView和Adapter數(shù)據(jù)適配器的簡(jiǎn)單介紹,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04Android實(shí)現(xiàn)淘寶底部圖標(biāo)導(dǎo)航欄
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)淘寶底部圖標(biāo)導(dǎo)航欄,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12Android開發(fā)中通過(guò)手機(jī)號(hào)+短信驗(yàn)證碼登錄的實(shí)例代碼
最近在開發(fā)一個(gè)android的項(xiàng)目,需要通過(guò)獲取手機(jī)驗(yàn)證碼來(lái)完成登錄功能,接下來(lái)通過(guò)實(shí)例代碼給大家分享手機(jī)號(hào)+短信驗(yàn)證碼登錄的實(shí)現(xiàn)方法,需要的的朋友參考下吧2017-05-05Android編程實(shí)現(xiàn)拍照功能的2種方法分析
這篇文章主要介紹了Android編程實(shí)現(xiàn)拍照功能的2種方法,結(jié)合具體實(shí)例形式對(duì)比分析了Android通過(guò)調(diào)用系統(tǒng)攝像頭及程序調(diào)用照相機(jī)功能兩種實(shí)現(xiàn)技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-07-07Android中Fragment的加載方式與數(shù)據(jù)通信詳解
本文主要介紹了Android中Fragment的加載方式與數(shù)據(jù)通信的相關(guān)知識(shí)。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-03-03Android?Flutter中Offstage組件的使用教程詳解
這篇文章主要為大家詳細(xì)介紹了Android?Flutter中Offstage組件的使用教程,文中的示例代碼講解詳細(xì),對(duì)我們了解Flutter有一定的幫助,需要的可以參考一下2023-02-02Android中使用Expandablelistview實(shí)現(xiàn)微信通訊錄界面
本文主要介紹了Android中使用Expandablelistview實(shí)現(xiàn)微信通訊錄界面(完善防微信APP)的方法,具有一定的參考價(jià)值,下面跟著小編一起來(lái)看下吧2016-12-12