首页 > 解决方案 > Android - ViewModel、LiveData、Room 和 Retrofit 以及协程放在 kotlin 中

问题描述

我正在学习 Android,我坚持让所有这些组件一起工作。我说的是 ViewModel、LiveData、Room、Retrofit 和协程。

所以,我想要实现的是:

当应用程序启动时,我想检查用户是否登录。如果没有登录,我什么也不做,如果他登录了,我在主要活动中记录他的名字。用户可以通过另一个 api 调用进行身份验证。然后,当用户更新时,主要活动应该反映用户的变化。我在这里https://developer.android.com/jetpack/guide部分遵循了谷歌代码实验室,但它不完整,我迷失了。

以下是代码块:

用户道.kt

使用 Room DAO 进行数据库查询

@Dao
interface UserDao {

    @Query("SELECT * FROM user WHERE id = :userId")
    fun get(userId: Long): LiveData<UserEntity?>

    @Insert(onConflict = REPLACE)
    suspend fun insert(userEntity: UserEntity)

    @Query("SELECT COUNT(*) FROM user WHERE last_update >= :timeout")
    suspend fun hasUser(timeout: Long): Boolean
}

用户服务.kt

这是一个使用 Retrofit 的简单 api 调用

interface UserService {

    @GET("users/current-user")
    suspend fun getUser(): Response<User>
}

用户存储库.kt

主要活动应在加载时调用 get 并刷新用户(调用 api 并检索用户 id),将用户 id 保存在 savedStateHandler 中,并从刚刚保存的数据库中检索用户。但是在第一次启动时,savedStateHandler 是空的。当我从 api 获取用户时,我将其存储在数据库中,但userId传递给的变量userDao.get(userId)仍然为 null,因此没有从数据库中加载任何内容。我应该在何时何地将用户的 ID 保存在 savedStateHandler 中?我在存储库中没有引用它。此外,当应用程序启动时,不会触发调用。如何从活动中触发它?不确定在可变实时数据变量中设置用户的位置以触发活动中的观察者。

suspend fun insert(userEntity: UserEntity) {
    userDao.insert(userEntity)
}

suspend fun get(userId: Long): LiveData<UserEntity?> {
    refreshUser()
    return userDao.get(userId)
}

private suspend fun refreshUser() = withContext(Dispatchers.IO) {
    launch {
        // Check if user data was fetched recently.
        val userExists = userDao.hasUser(FRESH_TIMEOUT)

        if (!userExists) {
            // Refreshes the data.
            val response = userService.getUser()
            try {
                if (response.isSuccessful) {
                    Log.i("user", "Success ${response.code()} - ${response.message()}")
                    // Updates the database. The LiveData object automatically
                    // refreshes, so we don't need to do anything else here.
                    val body: User? = response.body()
                    val user = UserEntity(
                        body!!.id,
                        body.email,
                        body.name.firstName,
                        body.name.lastName,
                        body.telephone,
                        body.dateOfBirth,
                        System.currentTimeMillis() / 1000
                    )
                    userDao.insert(user)
                } else {
                    Log.w(
                        "user",
                        "Not successful ${response.code()} - ${response.message()}"
                    )
                }
            } catch (e: HttpException) {
                Log.e("user", "Exception ${response.code()} - ${response.message()}")
            }
        }
    }
}

用户视图模型.kt

class UserViewModel @ViewModelInject constructor(
      private val repository: UserRepository,
      @Assisted private val savedStateHandle: SavedStateHandle) : ViewModel() {

    fun insert(userEntity: UserEntity) = viewModelScope.launch {
        repository.insert(userEntity)
    }

    private val userId: Long? = savedStateHandle["uid"]

    val user: LiveData<UserEntity?>
        get() = liveData { repository.get(userId) }
}

MainActivity.kt

    viewModel.user.observe(this) { user ->
        Log.i("USER IS LOGGED", user.toString())
        Toast.makeText(this, "HELLO ${user?.email}!!", Toast.LENGTH_SHORT).show()
    }

标签: androidkotlinandroid-roomandroid-livedatakotlin-coroutines

解决方案


private val userId: Long? = savedStateHandle["uid"]

val user: LiveData<UserEntity?>
   get() = liveData { repository.get(userId) }

应该

private val userId: MutableLiveData<Long> = savedStateHandle.getLiveData("uid")

val user: LiveData<UserEntity?>
    get() = userId.switchMap { userId -> liveData { repository.get(userId) } }

推荐阅读