当前位置: 首页 > 知识库问答 >
问题:

Android-ViewModel,LiveData,Room和改造以及协程在kotlin中放在一起

太叔昆
2023-03-14

我正在学习Android,我一直致力于让所有这些组件协同工作。我说的是ViewModel、LiveData、Room、改装和协同程序。

因此,我想实现以下目标:

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

下面是代码块:

UserDao.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

这是一个使用改进的简单api调用

interface UserService {

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

用户存储库。kt

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

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()}")
            }
        }
    }
}

UserViewModel.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) }
}

主要活动。kt

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

共有1个答案

郭浩穰
2023-03-14
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) } }
 类似资料:
  • 我想测试我的数据库层,我发现自己陷入了一种第22条军规的境地。 测试用例由两件事组成: 保存一些实体 加载实体并断言数据库映射按预期工作 简而言之,问题在于: 是一种方法,这意味着它需要在 我的类是这样的: 测试用例是: 是的一个扩展函数,基本上是从上面的链接复制粘贴的 测试这个场景的正确方法是什么?在开发数据库映射层时,我需要这些类型的测试,以确保一切都如我所期望的那样工作。

  • 对于LiveData和MVVM架构,我完全是个新手。我想知道如何观察一个实时数据 我通过以下方式从我的房间数据库中获取实时数据: 我希望ViewModel中的另一个变量,,在列表返回空(null)时进行更新。这将用于从Visible更新片段中的ImageView。去看电视了。看得见的 我如何检查如果是空的同步? 我四处阅读,看到一些人说使用,但是架构指南明确建议不要使用ViewModel中的任何观

  • 我试图解决一个问题,但没有成功。每当数据库(数据库室)中特定模型的记录发生变化时,我想更新我的回收器视图。我使用ViewModel来处理模型数据,记录列表存储在LiveData中。 数据库 模型 刀 视图模型 碎片 现在,我的问题是为什么观察者只被调用一次(在片段的开始),然后没有被再次调用?我如何让观察者不断地倾听数据库中的变化(插入,更新,删除),以便我的回收器视图可以立即更新?非常感谢任何建

  • 我读了文档,但在某些部分变得迷惑了。 ViewModel对象被设计为比views或LifeCycleOwners的特定实例化更长寿。这种设计还意味着您可以编写测试以更容易地覆盖ViewModel,因为它不知道视图和生命周期对象。ViewModel对象可以包含LifecycleObservers,如LiveData对象。但是,ViewModel对象绝不能观察到生命周期感知的可观察对象(如LiveDa

  • 因此,我使用LiveData和ViewModel设置获取和插入数据的功能,并使用Room数据库保存数据。 将数据插入数据库后,我的RecyclerView不会更新数据。 RecyclerAdapterTransaksi.kt 使用RecyclerView显示数据库数据的片段 Pemasukkanframent。kt 使用LiveData的ViewModel类 TransaksiViewModel.

  • 我正在努力把这个改型班翻译成科特林。它基本上是一个作为客户机工作的单例,我不确定我的Kotlin实现。UserAPI和ProfileAPI只是接口。 这是我的Kotlin实现。正如我所理解的,当类实例化时,init块将首先执行。 但有些事情告诉我,这不是正确的方法,或者可以做得更好。这里有什么我可以改进的吗? 更新!!! 可以吗?