[英]How to update my composable upon service property update?
我正在使用服務定位器(如https://developer.android.com/training/dependency-injection#di-alternatives中所建議的那樣,但稍后我會切換到適當的 DI,我保證)在我的應用程序中處理身份驗證。 我有一個身份驗證服務,它具有我使用logIn
和logOut
方法設置和取消設置的user
屬性
我希望我的ContentView
對auth.user
中的更改做出反應,但我不太清楚如何。 我已經嘗試by remember { mutableStateOf() }
將其包裝,但我在登錄時沒有看到任何更新..知道我錯過了什么嗎?
在此先感謝(下面的片段)
@Composable
fn ContentView() {
val auth = ServiceLocator.auth
var loggedInUser: User? by remember { mutableStateOf(auth.user) } // <-- I would like my composable to react to changes to auth.user
if (loggedInUser) {
ViewA()
} else {
ViewB()
}
}
object ServiceLocator {
val auth = AuthenticationService()
}
class AuthenticationService {
var user: User? = null
fun logIn() {
// sets user...
}
fun logOut() {
// undefs user...
}
在此行的代碼段中
var loggedInUser: User? by remember { mutableStateOf(auth.user) }
您正在創建一個MutableState<User?>
的實例,其初始值是當時由auth.user
引用的值。 由於remember { }
,這種初始化僅在可組合的ContentView
進入組合時發生,然后MutableState
實例在重新組合中被記住並重用。
如果您稍后更改變量auth.user
不會發生重組,因為存儲在loggedInUser
(處於可變狀態)中的值沒有更改。
mutableStateOf
的文檔解釋了這個調用在幕后的實際作用
返回使用傳入值初始化的新
MutableState
。
MutableState
class 是一個單值持有者,其讀寫被 Compose 觀察。 此外,對它的寫入作為Snapshot
系統的一部分進行處理。
讓我們剖析一下這條信息。
返回使用傳入值初始化的新 MutableState。
調用mutableStateOf
返回一個MutableState
實例,該實例使用作為參數傳遞的值進行初始化。
MutableState class 是單值持有者
此 class 的每個實例都存儲一個值 state。 它可能會出於實現目的存儲其他值,但它僅公開 state 的單個值。
Compose 觀察到其讀取和寫入
Compose 觀察發生在MutableState
實例上的讀取和寫入
這是您錯過的信息。 寫入需要發生在 MutableState 的實例(在您的情況下為loggedInUser
),而不是發生在已作為初始值傳入的變量(在您的情況下為auth.user
)。
如果你仔細想想,Kotlin 中沒有內置機制來觀察變量的變化,所以 Compose 必須有一個包裝器才能觀察到變化是可以理解的。 而且我們必須通過包裝器更改 state 而不是直接更改變量。
知道所有你可以將可變的 state 移動到AuthenticationService
中,一切都會奏效
import androidx.compose.runtime.mutableStateOf
class AuthenticationService {
var user: User? by mutableStateOf(null)
private set
// ... rest of the service
}
@Composable
fun ContentView() {
val auth = ServiceLocator.auth
// no remember { } block this time because now the MutableState reference is being kept by
// the AuthenticationService so it won't reset on recomposition
val loggedInUser = auth.user
if (loggedInUser != null) {
ViewA()
} else {
ViewB()
}
}
但是,現在您的AuthenticationService
依賴於mutableStateOf
,因此依賴於您可能想要避免的 Composable 運行時。 “服務”(或存儲庫)不需要了解有關 UI 實現的詳細信息。
還有其他選項可以跟蹤 state 更改並且不依賴於 Compose 運行時。 來自文檔部分Compose 和其他庫
Compose 附帶了適用於 Android 最流行的基於流的解決方案的擴展。 這些擴展中的每一個都由不同的工件提供:
Flow.collectAsState()
不需要額外的依賴項。 (因為它是kotlinx-coroutines-core
的一部分)
LiveData.observeAsState()
包含在androidx.compose.runtime:runtime-livedata:$composeVersion
工件中。
Observable.subscribeAsState()
包含在androidx.compose.runtime:runtime-rxjava2:$composeVersion
或androidx.compose.runtime:runtime-rxjava3:$composeVersion
工件中。這些工件注冊為偵聽器,並將值表示為
State
。 每當發出新值時,Compose 都會重新組合 UI 中使用該state.value
的那些部分。
使用 Kotlin MutableStateFlow
的示例
// No androidx.compose.* dependencies anymore
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
class AuthenticationService {
private val user = MutableStateFlow<User?>(null)
val userFlow = user.asStateFlow()
fun logIn() {
user.value = User(/* potential parameters */)
}
fun logOut() {
user.value = null
}
}
然后在可組合中,我們將流收集為 state。
import androidx.compose.runtime.collectAsState
@Composable
fun ContentView() {
val auth = ServiceLocator.auth
val loggedInUser = auth.userFlow.collectAsState().value
if (loggedInUser != null) {
ViewA()
} else {
ViewB()
}
}
要了解有關在 Compose 中使用 state 的更多信息,請參閱有關管理 State的文檔部分。 這是能夠在 Compose 中使用 state 並有效觸發重組的基本信息。 它還涵蓋了state 吊裝的基礎知識。 如果您更喜歡親身體驗 ,請參閱 Jetpack Compose 中 State 的代碼實驗室。
最終(當您的應用程序變得復雜時)您可能希望在您的服務/存儲庫層和您的 UI 層(可組合)之間放置另一層。 將保存和管理 UI state 的層,因此您將能夠涵蓋正面結果(成功登錄)和負面結果(登錄失敗)。
如果您采用 MVVM (Model-View-ViewModel) 方式或 MVI (Model-View-Intent) 方式,則該層將被ViewModel
覆蓋。 在這種情況下,可組合項僅管理一些瞬態 UI state 自己,同時他們獲得(或觀察)UI state 的 rest 並從 VM 調用 VM 以執行操作。 然后,VM 與服務/存儲庫層交互並相應地更新 UI state。 隨着復雜性的增加,有關處理 state 的介紹在來自 Google 的視頻中關於使用 Jetpack Compose 的自動 state 觀察。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.