我在ViewModel + Kotlin协程+ LiveData中的代码有问题



我有一个ViewModel

@HiltViewModel
class LoginViewModel @Inject constructor(
private val apiRepository: ApiRepository
) : ViewModel() {
private val account = MutableLiveData<String>("123")
private val password = MutableLiveData<String>("123")
val message: MutableLiveData<String> = MutableLiveData()
var loginResult: LiveData<Resource<UserInfo>> = MutableLiveData()
fun signIn() {
if (TextUtils.isEmpty(account.value)) {
message.postValue("Please enter your account")
return
}
if (TextUtils.isEmpty(password.value)) {
message.postValue("please enter your password")
return
}
// In this code, it doesn’t work. I think it’s because I didn’t observe it.  
// Is there any better way to write it here?
loginResult = apiRepository.signIn(account.value!!, password.value!!)
}
fun inputAccount(accountValue: String) {
account.value = accountValue
}
fun inputPassword(passwordValue: String) {
password.value = passwordValue
}
}

这是我的界面

@AndroidEntryPoint
class LoginActivity : BaseActivity<ActivityLoginBinding>() {
private val viewModel: LoginViewModel by viewModels()
......
override fun initEvent() {
binding.account.editText!!.addTextChangedListener { viewModel.inputAccount(it.toString()) }
binding.password.editText!!.addTextChangedListener { viewModel.inputPassword(it.toString()) }
binding.signIn.setOnClickListener {
viewModel.signIn()
}
}
override fun setupObservers() {
viewModel.message.observe(this) {
Snackbar.make(binding.root, it, Snackbar.LENGTH_SHORT).show()
}
/**
* There will be no callback here, I know it’s because I’m observing
* `var loginResult: LiveData<Resource<UserInfo>> = MutableLiveData()`
* instead of `apiRepository.signIn(account.value!!, password.value!!)` 
* because it was reassigned
*/
viewModel.loginResult.observe(this) {
Log.d("TAG", "setupObservers: $it")
}
}
}
所以我稍微调整了一下代码LoginViewModel.signIn
fun signIn(): LiveData<Resource<UserInfo>>? {
if (TextUtils.isEmpty(account.value)) {
message.postValue("Please enter your account")
return null
}
if (TextUtils.isEmpty(password.value)) {
message.postValue("please enter your password")
return null
}
return apiRepository.signIn(account.value!!, password.value!!)
}

LoginActivity.initEvent

override fun initEvent() {
binding.signIn.setOnClickListener {
viewModel.signIn()?.observe(this) {
Log.d("TAG", "setupObservers: $it")
}
}
}

我查看了LiveData的官方文档,初始化时都调用了livedata{}。没有重新分配,但是如果您登录,您不能直接启动应用程序并请求网络。

协程doucument

虽然我最终达到了我的结果,但我认为这不是最好的做法,所以我想寻求帮助!

  • 补充代码ApiRepository
class ApiRepository @Inject constructor(
private val apiService: ApiService
) : BaseRemoteDataSource() {
fun signIn(account: String, password: String) =
getResult { apiService.signIn(account, password) }
}

BaseRemoteDataSource

abstract class BaseRemoteDataSource {
protected fun <T> getResult(call: suspend () -> Response<T>): LiveData<Resource<T>> =
liveData(Dispatchers.IO) {
try {
val response = call.invoke()
if (response.isSuccessful) {
val body = response.body()
if (body != null) emit(Resource.success(body))
} else {
emit(Resource.error<T>(" ${response.code()} ${response.message()}"))
}
} catch (e: Exception) {
emit(Resource.error<T>(e.message ?: e.toString()))
}
}
}

或者我这样写

fun signIn() {
if (TextUtils.isEmpty(account.value)) {
message.postValue("Please enter your account")
return
}
if (TextUtils.isEmpty(password.value)) {
message.postValue("please enter your password")
return
}
viewModelScope.launch {
repository.signIn(account.value, password.value).onEach {
loginResult.value = it
}
}
}

但我认为这是不完美的

最新更新