LiveData наблюдает, что не работает после обновления базы данных номеров

#android #kotlin #viewmodel

Вопрос:

Мое приложение содержит базу данных рецептов коктейлей, которые оно загружает с помощью вызова api модернизации, все это работает хорошо. Чтобы сосредоточиться на том, в чем заключается моя проблема, мой вариант использования-пользователь, добавляющий коктейль в список. Это делается с помощью диалогового фрагмента, и здесь отображается диалоговый фрагмент, выполняется транзакция, диалоговый фрагмент исчезает, и база данных комнат обновляется. Однако фрагмент коктейля не получает обновления — если вы переместитесь в сторону и обратно, обновление будет видно, поэтому я знаю, что транзакция сработала должным образом. Одна вещь, которую япросто заметил, что если я поверну устройство, обновление также будет подхвачено.

Вот соответствующий раздел моего фрагмента:

 class CocktailDetailFragment : BaseFragment<CocktailDetailViewModel, FragmentCocktailDetailBinding, CocktailDetailRepository>() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewModel.cocktail.observe(viewLifecycleOwner, Observer {
            when(it){
                is Resource.Success -> {
                    updateCocktail(it.value)
                }
            }
        })
    }

private fun updateCocktail(cocktail: Cocktail) {
        with(binding){
            detailCocktailName.text = cocktail.cocktailName
            //...
            //this is the piece of functionality i'm expecting the LiveData observer to execute and change the drawable
            if(cocktail.numLists > 0) {
                detailCocktailListImageView.setImageResource(R.drawable.list_filled)
            } else {
                detailCocktailListImageView.setImageResource(R.drawable.list_empty)
            }
        }
    }
    override fun getViewModel() = CocktailDetailViewModel::class.java

    override fun getFragmentBinding(
        inflater: LayoutInflater,
        container: ViewGroup?
    ) = FragmentCocktailDetailBinding.inflate(inflater,container,false)

    override fun getFragmentRepository(): CocktailDetailRepository {
        val dao = GoodCallDatabase(requireContext()).goodCallDao()
        return CocktailDetailRepository(dao)
    }
}
 

Базовый фрагмент:

 abstract class BaseFragment<VM: BaseViewModel, B: ViewBinding, R: BaseRepository>: Fragment() {

    protected lateinit var userPreferences: UserPreferences
    protected lateinit var binding: B
    protected lateinit var viewModel: VM
    protected val remoteDataSource = RemoteDataSource()


    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        userPreferences = UserPreferences(requireContext())
        binding = getFragmentBinding(inflater, container)
        val factory = ViewModelFactory(getFragmentRepository())
        viewModel = ViewModelProvider(this, factory).get(getViewModel())
        return binding.root
    }

    abstract fun getViewModel() : Class<VM>

    abstract fun getFragmentBinding(inflater: LayoutInflater, container: ViewGroup?): B

    abstract fun getFragmentRepository(): R

}
 

ViewModelFactory:

 class ViewModelFactory(
    private val repository: BaseRepository
): ViewModelProvider.NewInstanceFactory() {
    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return when{
            modelClass.isAssignableFrom(CocktailDetailViewModel::class.java) -> CocktailDetailViewModel(repository as CocktailDetailRepository) as T
            else -> throw IllegalArgumentException("ViewModel class not found")
        }
    }
}
 

ViewModel:

 class CocktailDetailViewModel(
    private val repository: CocktailDetailRepository
): BaseViewModel(repository) {
    private val _cocktail: MutableLiveData<Resource<Cocktail>> = MutableLiveData()
    val cocktail: LiveData<Resource<Cocktail>>
        get() = _cocktail

    fun getCocktailByCocktailId(cocktailId: Int) = viewModelScope.launch {
        _cocktail.value = Resource.Loading
        _cocktail.value = repository.getCocktail(cocktailId)
    }
}
 

Repository:

 class CocktailDetailRepository(
    private val dao: GoodCallDao
):BaseRepository(dao) {
    suspend fun getCocktail(cocktailId: Int) = safeApiCall {
        dao.getCocktail(cocktailId)
    }
}
 

Безопасный вызов Api (я использую это, чтобы вызовы db/api выполнялись при вводе-выводе):

 interface SafeApiCall {
    suspend fun <T> safeApiCall(
        apiCall: suspend () -> T
    ): Resource<T> {

        return withContext(Dispatchers.IO) {
            try {
                Resource.Success(apiCall.invoke())
            } catch (throwable: Throwable) {
                when (throwable) {
                    is HttpException -> {
                        Resource.Failure(false, throwable.code(), throwable.response()?.errorBody())
                    }
                    else -> {
                        Resource.Failure(true, null, null)
                    }
                }
            }
        }
    }
}
 

Ресурс:

 sealed class Resource<out T> {
    data class Success<out T>(val value: T) : Resource<T>()
    data class Failure(
        val isNetworkError: Boolean,
        val errorCode: Int?,
        val errorBody: ResponseBody?
    ): Resource<Nothing>()
    object Loading: Resource<Nothing>()
}
 

Дао:

 @Query("SELECT * FROM cocktail c WHERE c.cocktail_id = :cocktailId")
    suspend fun getCocktail(cocktailId: Int): Cocktail
 

Заранее благодарю вас за любую помощь! Учитывая проблему и то, как работает приложение, я считаю, что предоставил все соответствующие части, но, пожалуйста, сообщите, требуется ли для этого больше моего кода.

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

1. здесь не нужно объяснять свой уровень опыта, хороший ответ должен быть понятен всем, и если вы не можете понять ответ, вы можете добавить к нему комментарии

2. ваша проблема, вероятно, нигде здесь не кроется, потому что этот код, о котором вы уже сказали, работает, верно ? вместо CocktailDetailFragment того , есть ли у вас CocktailFragment место, где вы ожидаете этого обновления, где оно не отображается ?

3. В viewModel.cocktail.observe , почему бы не проверить, получили ли вы Loading или Failure ?

4. @a_local_nobody — это работает, но не обновляется при обновлении отображаемых данных CocktailDetailFragment . У меня нет CocktailFragment , происходит отсутствие обновления LiveData CocktailDetailFragment . Как уже упоминалось, ожидаемое обновление произойдет без перехода от фрагмента, но поворота устройства.

5. @rupinderjeet — в основном ленивый в данный момент, просто пытаюсь заставить работать все функции, мне нужно решать задачи по обработке этих состояний во многих местах.