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