状态值更改时屏幕不会重新组合 - Jetpack Compose

Har*_*abh 7 android kotlin kotlin-coroutines android-jetpack-compose compose-recomposition

这是一个视频通话屏幕。它需要令牌和通道名称才能工作,需要将其传递给初始化调用引擎。我将它们存储在用作可变状态的数据类中。

屏幕状态数据类

@Keep
data class CallScreenState(
    val callerId: Int? = null,
    val recieverId: Int? = null,
    val chatRoom: ChatRoom.Data? = null,
    val rtcToken: AgoraTokenResponse.TokenData? = null
)
Run Code Online (Sandbox Code Playgroud)

并在视图模型初始化状态下通过以下代码:

var callScreenState by mutableStateOf(CallScreenState())
Run Code Online (Sandbox Code Playgroud)

在聊天室和令牌 api 成功响应的视图模型中,状态将使用此代码进行更新。

callScreenState = callScreenState.copy(
                                chatRoom = chatRoom.data,//from response
                                rtcToken = token.data   //from response
                            )
Run Code Online (Sandbox Code Playgroud)

从这里开始,预计将使用 chatRoom 和 rtcToken 的新更新值重新组合屏幕。

并且在可组合中

val screenState = remember {
    viewModel.callScreenState
}
Run Code Online (Sandbox Code Playgroud)

此屏幕状态用于将值传递给 init 引擎

val mEngine = remember {
    initEngine(
        context,
        object : IRtcEngineEventHandler() {
            override fun onJoinChannelSuccess(channel: String?, uid: Int, elapsed: Int) {
                Timber.e("hhp-CallScreen onJoinChannelSuccess channel:$channel,uid:$uid,elapsed:$elapsed")
            }

            override fun onUserJoined(uid: Int, elapsed: Int) {
                Timber.e("hhp-CallScreen onUserJoined:$uid")
                val desiredUserList = remoteUserMap.toMutableMap()
                desiredUserList[uid] = null
                remoteUserMap = desiredUserList.toMap() as HashMap<Int, TextureView?>
            }

            override fun onUserOffline(uid: Int, reason: Int) {
                Timber.e("hhp-CallScreen onUserOffline:$uid")
                val desiredUserList = remoteUserMap.toMutableMap()
                desiredUserList.remove(uid)
                remoteUserMap = desiredUserList.toMap() as HashMap<Int, TextureView?>
            }

            override fun onNetworkQuality(uid: Int, txQuality: Int, rxQuality: Int) {
                Timber.e("hhp-CallScreen onNetworkQuality $uid $txQuality $rxQuality")

            }
        },
        screenState.chatRoom?.channelName ?: "",  //Not recomposing when value changes in viewmodel
        viewModel.userRole,
        token = screenState.rtcToken?.token ?: "" //Not recomposing when value changes in viewmodel
    )
}
Run Code Online (Sandbox Code Playgroud)

这是initEngine函数的创建

fun initEngine(
    current: Context,
    eventHandler: IRtcEngineEventHandler,
    channelName: String,
    userRole: String,
    token: String
): RtcEngine =
    RtcEngine.create(current, BuildConfig.AGORA_APPID, eventHandler).apply {
        enableVideo()
        setChannelProfile(1)
        if (userRole == "Broadcaster") {
            setClientRole(1)
        } else {
            setClientRole(0)
        }
        //Expected to be recomposed when screen state value updated with new values
        joinChannel(token, channelName, "", 0)
    }
Run Code Online (Sandbox Code Playgroud)

我一开始就明白,在 api 调用之前,屏幕状态中的通道名称和令牌为空。一旦用于获取令牌和聊天室的 api 成功,屏幕状态就会从 viewmodel 更新,我希望 initEngine fun 会再次被调用,因为它应该重新组合。但事实并非如此。我错过了什么吗?当屏幕 sctate 内的通道名称值发生变化时,如何使其重新组合?

z.g*_*g.y 2

我无法理解您的整个用例,但您是否尝试过key为您的指定 a remember

当屏幕 sctate 内的通道名称值发生变化时,如何使其重新组合?

您可以尝试其中任何一个,尽管我不确定它们是否会解决您的问题,但是当您提供 akey并且remember它发生变化时,它会发生变化re-calculate,假设先前计算channelName使用的 下remember's一个计算将有所不同re-composition

这,

val screenState = remember(key1 = channelname) {
    viewModel.callScreenState
}
Run Code Online (Sandbox Code Playgroud)

或这个

val mEngine = remember(key1 = channelname) { ... }
Run Code Online (Sandbox Code Playgroud)