Android — ViewModel, LiveData, Room, модернизация и сопрограммы, собранные вместе в kotlin

#android #kotlin #android-room #android-livedata #kotlin-coroutines

#Android #kotlin #android-room #android-livedata #kotlin-сопрограммы

Вопрос:

Я изучаю Android, и я застрял в том, чтобы заставить все эти компоненты работать вместе. Я говорю о ViewModel, LiveData, Room, модернизации и сопрограммах.

Итак, я хочу добиться следующего:

Когда приложение запускается, я хочу проверить, вошел ли пользователь в систему или нет. Если он не зарегистрирован, я ничего не делаю, если он вошел в систему, я регистрирую его имя в основном действии. Пользователь может пройти аутентификацию с помощью другого вызова api. Затем, когда пользователь обновляется, основное действие должно отражать изменение пользователя. Я частично следил за Google codelab здесь 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
}
 

UserService.kt

Это просто вызов api с использованием модернизации

 interface UserService {

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

UserRepository.kt

основное действие должно вызывать get при загрузке и обновлять пользователя (вызывать api и извлекать идентификатор пользователя), сохранять идентификатор пользователя в savedStateHandler и извлекать пользователя из базы данных, где он только что был сохранен. Но при самом первом запуске savedStateHandler пуст. Когда я получаю пользователя из API, я сохраняю его в базе данных, но userId переданная переменная по userDao.get(userId) -прежнему равна нулю, поэтому из базы данных ничего не загружается. Где и когда я должен сохранять идентификатор пользователя в savedStateHandler? У меня нет ссылки на это в репозитории. Также при запуске приложения вызов не выполняется. Как я могу запустить его из activity? Не уверен, где указать пользователя в изменяемой переменной live data, чтобы вызвать наблюдателя в действии.

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

MainActivity.kt

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

Комментарии:

1. кажется, вы всегда хотите получить «текущего» пользователя, я правильно понял? refreshUser() не принимает никаких аргументов, поэтому IMO не должен UserRepository.get() . Вы можете сделать refreshUser , чтобы вернуть вставленный идентификатор private suspend fun refreshUser(): Long? = withContext(Dispatchers.IO) { , а затем использовать его для извлечения идентификатора пользователя из DAO

Ответ №1:

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