观察回调没有被Kotlin Coroutines流动和Livedata触发吗?

发布于 2025-01-30 02:55:12 字数 2154 浏览 5 评论 0 原文

我是Android开发的新手,并试图从各种示例项目中了解Coroutines和 livedata 。当用户输入用户名和密码时,我目前已经设置了一个函数来调用我的API。但是,在按下1按钮后,该应用似乎堵塞了,我无法将另一个API调用好像卡在待处理过程中一样。

这是我第一个用大量想法制作的Android应用程序,所以请让我知道我在哪里犯了错误!

活动:

binding.bLogin.setOnClickListener {
        val username = binding.etUsername.text.toString()
        val password = binding.etPassword.text.toString()
        viewModel.userClicked(username, password).observe(this, Observer {
            it?.let { resource ->
                when (resource.status) {
                    Status.SUCCESS -> {
                        print(resource.data)
                    }
                    Status.ERROR -> {
                        print(resource.message)
                    }
                    Status.LOADING -> {
                        // loader stuff
                    }
                }
            }
        })
 }

ViewModel:

fun userClicked(username: String, password: String) = liveData(dispatcherIO) {
    viewModelScope.launch {
        emit(Resource.loading(data = null))
        try {
            userRepository.login(username, password).apply {
                emit(Resource.success(null))
            }
        } catch (exception: Exception) {
            emit(Resource.error(exception.message ?: "Error Occurred!", data = null))
        }
    }
}

存储库:

@WorkerThread
suspend fun login(
    username: String,
    password: String
): Flow<Resource<String?>> {
    return flow {
        emit(Resource.loading(null))
        api.login(LoginRequest(username, password)).apply {
            this.onSuccessSuspend {
                data?.let {
                    prefs.apiToken = it.key
                    emit(Resource.success(null))
                }
            }
        }.onErrorSuspend {
            emit(Resource.error(message(), null))
        }.onExceptionSuspend {
            emit(Resource.error(message(), null))
        }
    }.flowOn(dispatcherIO)
}

API:

suspend fun login(@Body request: LoginRequest): ApiResponse<Auth>

I'm new to Android development and trying to understand Coroutines and LiveData from various example projects. I have currently setup a function to call my api when the user has input a username and password. However after 1 button press, the app seems to jam and I can't make another api call as if its stuck on a pending process.

This is my first android app made with a mash of ideas so please let me know where I've made mistakes!

Activity:

binding.bLogin.setOnClickListener {
        val username = binding.etUsername.text.toString()
        val password = binding.etPassword.text.toString()
        viewModel.userClicked(username, password).observe(this, Observer {
            it?.let { resource ->
                when (resource.status) {
                    Status.SUCCESS -> {
                        print(resource.data)
                    }
                    Status.ERROR -> {
                        print(resource.message)
                    }
                    Status.LOADING -> {
                        // loader stuff
                    }
                }
            }
        })
 }

ViewModel:

fun userClicked(username: String, password: String) = liveData(dispatcherIO) {
    viewModelScope.launch {
        emit(Resource.loading(data = null))
        try {
            userRepository.login(username, password).apply {
                emit(Resource.success(null))
            }
        } catch (exception: Exception) {
            emit(Resource.error(exception.message ?: "Error Occurred!", data = null))
        }
    }
}

Repository:

@WorkerThread
suspend fun login(
    username: String,
    password: String
): Flow<Resource<String?>> {
    return flow {
        emit(Resource.loading(null))
        api.login(LoginRequest(username, password)).apply {
            this.onSuccessSuspend {
                data?.let {
                    prefs.apiToken = it.key
                    emit(Resource.success(null))
                }
            }
        }.onErrorSuspend {
            emit(Resource.error(message(), null))
        }.onExceptionSuspend {
            emit(Resource.error(message(), null))
        }
    }.flowOn(dispatcherIO)
}

API:

suspend fun login(@Body request: LoginRequest): ApiResponse<Auth>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

柠北森屋 2025-02-06 02:55:12

您无需在 livedata 建造者中启动coroutine,它已经是 suppend ,因此您可以在此处调用 suppend 功能:

fun userClicked(username: String, password: String) = liveData(dispatcherIO) {
    emit(Resource.loading(data = null))
    try {
        userRepository.login(username, password).apply {
            emit(Resource.success(null))
        }
    } catch (exception: Exception) {
        emit(Resource.error(exception.message ?: "Error Occurred!", data = null))
    }
}

如果您想使用使用使用 flow 您可以使用 aslivedata 函数:

fun userClicked(username: String, password: String): LiveData<Resource<String?>> {
    return userRepository.login(username, password).asLiveData()
}

但是我不建议将 livedata flow 流中混合。我建议仅使用 Flow

仅使用 flow

// In ViewModel:

fun userClicked(username: String, password: String): Flow<Resource<String?>> {
    return userRepository.login(username, password)
}

// Activity

binding.bLogin.setOnClickListener {
        val username = binding.etUsername.text.toString()
        val password = binding.etPassword.text.toString()
        lifecycleScope.launch {
            viewModel.userClicked(username, password).collect { resource ->
                when (resource.status) {
                    Status.SUCCESS -> {
                        print(resource.data)
                    }
                    Status.ERROR -> {
                        print(resource.message)
                    }
                    Status.LOADING -> {
                        // loader stuff
                    }
                }
            }
        }
    }

删除暂停关键字从登录 repository 中的功能。

lifecyclescope docs

You don't need to launch a coroutine in liveData builder, it is already suspend so you can call suspend functions there:

fun userClicked(username: String, password: String) = liveData(dispatcherIO) {
    emit(Resource.loading(data = null))
    try {
        userRepository.login(username, password).apply {
            emit(Resource.success(null))
        }
    } catch (exception: Exception) {
        emit(Resource.error(exception.message ?: "Error Occurred!", data = null))
    }
}

If you want to use LiveDate with Flow you can convert Flow to LiveData object using asLiveData function:

fun userClicked(username: String, password: String): LiveData<Resource<String?>> {
    return userRepository.login(username, password).asLiveData()
}

But I wouldn't recommend to mix up LiveData and Flow streams in the project. I suggest to use only Flow.

Using only Flow:

// In ViewModel:

fun userClicked(username: String, password: String): Flow<Resource<String?>> {
    return userRepository.login(username, password)
}

// Activity

binding.bLogin.setOnClickListener {
        val username = binding.etUsername.text.toString()
        val password = binding.etPassword.text.toString()
        lifecycleScope.launch {
            viewModel.userClicked(username, password).collect { resource ->
                when (resource.status) {
                    Status.SUCCESS -> {
                        print(resource.data)
                    }
                    Status.ERROR -> {
                        print(resource.message)
                    }
                    Status.LOADING -> {
                        // loader stuff
                    }
                }
            }
        }
    }

Remove suspend keyword from the login function in Repository.

lifecycleScope docs.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文