天天看點

compose 程式設計思想

前言

compose已經熟悉了幾周了,跟着google sample進行了解時,對可組合函數參數清單如何聲明,事件向上,資料向下傳遞了解還是有偏差。重新了解了下compose程式設計思想

1.早期基于View 微件數:

更新界面:多個需要同時更新View,會遺漏。複雜的View微件更新會更新前面已經移除過微件的狀态

2.聲明性範式:
tips:mvvm 為資料驅動界面ui更新。 界面(Fragment,Activity)訂閱ViewModel中的LiveData,資料發生變化時通知界面進行重新整理。而lifecycle系列避免發生訂閱記憶體洩露而引入的輔助管理類。

compose:一個聲明性界面架構,【完全重新生成一個螢幕】

聲明性範式轉變

View微件數:在許多面向對象的指令式界面工具包中,您可以通過執行個體化微件樹來初始化界面。您通常通過Inflate XML 布局檔案來實作此目的。每個微件都維護自己的内部狀态,并且提供 getter 和 setter 方法,允許應用邏輯與微件進行互動。

Compose 的聲明性方法中:微件相對無狀态,并且不提供 setter 或 getter 函數,微件不會以對象形式提供,您可以通過調用帶有不同參數的同一可組合函數來更新界面

界面描述:自上而下

compose 程式設計思想
應用邏輯為頂級可組合函數提供資料,該函數(頂級可組合函數)通過調用其他可組合函數來使用這些資料描述界面,将适當的資料傳遞給這些可組合函數,并沿層次結構向下傳遞資料

事件響應:自下而上

compose 程式設計思想

例如:觸發onclick事件,這些事件應通知應用邏輯,應用邏輯随後可以改變應用的狀态。當狀态發生變化時,系統會使用新資料再次調用可組合函數。這會導緻重新繪制界面元素,此過程稱為“重組”。

單選按鈕點選,應用邏輯将單選按鈕狀态由check變為checked。狀态變化導緻compose函數 重組

可組合函數内不建議緩存臨時變量,包含匿名函數,除非特别簡單。建議将狀态和資料提取到可組合函數外面使可組合函數幂等

重組

1.切勿依賴于執行可組合函數所産生的附帶效應,因為可能會跳過函數的重組

附帶效應:對應用的其餘部分可見的任何更改

寫入共享對象的屬性
更新 ViewModel 中的可觀察項
更新共享偏好設定      

可組合函數可能會像每一幀一樣頻繁地重新執行,如果您需要執行成本高昂的操作(例如從共享偏好設定讀取資料),請在[背景協程]中執行,并将值結果作為參數傳遞給可組合函數。

本文檔讨論了您在 Compose 中程式設計時需要注意的事項:

可組合函數可以按任何順序執行:

可組合函數均應該是幂等的,不應該依賴聲明函數順序,臆想程式執行順序是序列化

可組合函數可以并行執行。

如果您看一下可組合函數的代碼,可能會認為這些代碼按其出現的順序運作。但其實未必是這樣。如果某個可組合函數包含對其他可組合函數的調用,這些函數可以按任何順序運作。Compose 可以選擇識别出某些界面元素的優先級高于其他界面元素,因而首先繪制這些元素。

例如,假設您有如下代碼,用于在标簽頁布局中繪制三個螢幕:

@Composable
fun ButtonRow() {
    MyFancyNavigation {
        StartScreen()
        MiddleScreen()
        EndScreen()
    }
}      

對 StartScreen、MiddleScreen 和 EndScreen 的調用可以按任何順序進行。這意味着,舉例來說,您不能讓 StartScreen() 設定某個全局變量(附帶效應)并讓 MiddleScreen() 利用這項更改。相反,其中每個函數都需要保持獨立。

可組合函數可以并行運作

Compose 可以通過并行運作可組合函數來優化重組。這樣一來,Compose 就可以利用多個核心,并以較低的優先級運作可組合函數(不在螢幕上)。

這種優化意味着,可組合函數可能會在背景線程池中執行。如果某個可組合函數對 ViewModel 調用一個函數,則 Compose 可能會同時從多個線程調用該函數。

為了確定應用正常運作,所有可組合函數都不應有附帶效應,而應通過始終在界面線程上執行的 onClick 等回調觸發附帶效應。

調用某個可組合函數時,調用可能發生在與調用方不同的線程上。這意味着,應避免使用修改可組合 lambda 中的變量的代碼,既因為此類代碼并非線程安全代碼,又因為它是可組合 lambda 不允許的附帶效應。

以下示例展示了一個可組合項,它顯示一個清單及其項數:

@Composable
fun ListComposable(myList: List<String>) {
    Row(horizontalArrangement = Arrangement.SpaceBetween) {
        Column {
            for (item in myList) {
                Text("Item: $item")
            }
        }
        Text("Count: ${myList.size}")
    }
}      

此代碼沒有附帶效應,它會将輸入清單轉換為界面。此代碼非常适合顯示小清單。不過,如果函數寫入局部變量,則這并非線程安全或正确的代碼:

@Composable
@Deprecated("Example with bug")
fun ListWithBug(myList: List<String>) {
    var items = 0

    Row(horizontalArrangement = Arrangement.SpaceBetween) {
        Column {
            for (item in myList) {
                Text("Item: $item")
                items++ // Avoid! Side-effect of the column recomposing.
            }
        }
        Text("Count: $items")
    }
}      

在本例中,每次重組時,都會修改 items。這可以是動畫的每一幀,或是在清單更新時。但不管怎樣,界面都會顯示錯誤的項數。是以,Compose 不支援這樣的寫入操作;通過禁止此類寫入操作,我們允許架構更改線程以執行可組合 lambda。

重組會跳過盡可能多的可組合函數和 lambda。

重組是樂觀的操作,可能會被取消:

隻要 Compose 認為某個可組合項的參數可能已更改,就會開始重組。重組是樂觀的操作,也就是說,Compose 預計會在參數再次更改之前完成重組。如果某個參數在重組完成之前發生更改,Compose 可能會取消重組,并使用新參數重新開始。

取消重組後,Compose 會從重組中舍棄界面樹。如有任何附帶效應依賴于顯示的界面,則即使取消了組成操作,也會應用該附帶效應。這可能會導緻應用狀态不一緻。

可組合函數可能會像動畫的每一幀一樣非常頻繁地運作
最佳做法都是使可組合函數保持快速、幂等且沒有附帶效應。