Поток сопрограмм не извлекает последние данные из Firestore после каких-либо изменений в коллекции

#android #firebase #google-cloud-firestore #kotlin-coroutines #kotlin-flow

#Android #firebase #google-cloud-firestore #kotlin-сопрограммы #kotlin-flow

Вопрос:

Я хочу получать последние данные из коллекции Firestore, но каждый раз терпел неудачу. Поскольку я использую MVVM repository , это выглядит так:

 class PurchaseRepository {

    private val mPurchaseItemsCollection =
        FirebaseFirestore.getInstance().collection(Constants.COLLECTION_PURCHASE)

    /**
     * Returns Flow of [State] which retrieves all PurchaseItems from cloud firestore collection.
     */
    fun getAllItems() = flow<State<List<PurchaseItem>>> {

        // Emit loading state
        emit(State.loading())

        val snapshot = mPurchaseItemsCollection.get().await()

        val items = snapshot.toObjects(PurchaseItem::class.java)

        // Emit success state with data
        emit(State.success(items))

    }.catch {
        // If exception is thrown, emit failed state along with message.
        emit(State.failed(it.message.toString()))
    }.flowOn(Dispatchers.IO)

}
 

и ViewModel выглядит:

 class CurrentMonthViewModel(private val repository: PurchaseRepository) : ViewModel() {    
    fun getAllItems() = repository.getAllItems()
}
 

и, наконец Fragment ,:

 class CurrentMonthFragment : Fragment() {

    private lateinit var currentMonthViewModel: CurrentMonthViewModel
    private var _binding: FragmentCurrentBinding? = null

    // This property is only valid between onCreateView and
    // onDestroyView.
    private val binding get() = _binding!!

    // Coroutine Scope
    private val uiScope = CoroutineScope(Dispatchers.Main)

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {

        _binding = FragmentCurrentBinding.inflate(inflater, container, false)
        val root: View = binding.root

        currentMonthViewModel = ViewModelProvider(this, CurrentViewModelFactory())
            .get(CurrentMonthViewModel::class.java)

        val recyclerView = binding.recyclerviewCurrent

        val adapter = CurrentMonthAdapter(root)
        recyclerView.adapter = adapter

        // Launch coroutine
        uiScope.launch {
            loadItems(root, adapter)
        }

        return root

    }

    private suspend fun loadItems(root: View, adapter: CurrentMonthAdapter) {
        currentMonthViewModel.getAllItems().collect {
            when(it){
                is State.Loading -> {
                    showToast(root, "Loading")
                }
                is State.Success -> {
                    Log.e("__DATA__", it.data.toString())
                    adapter.submitList(it.data)
                }
                is State.Failed -> {
                    Log.e("__DATA__", it.message)
                    showToast(root, "Failed!")
                }
            }
        }    
    }

    private fun showToast(root: View, message: String) {
        Toast.makeText(root.context, message, Toast.LENGTH_SHORT).show()
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}
 

с помощью приведенного выше кода приложение извлекает данные только при запуске или перезапуске, но не в режиме реального времени (означает, что если новый элемент добавлен в коллекцию с помощью firebase.console, он никогда не показывает новые обновления). Я попробовал и другой способ, создав другой метод в репозитории как:

 fun getAllItemsRealTime() = flow<State<List<PurchaseItem>>> {
    val items = mutableListOf<PurchaseItem>()
    mPurchaseItemsCollection.addSnapshotListener { value, error ->
        if (error !== null) {
            Log.e("addSnapshotListener","Error ${error.message}")
            return@addSnapshotListener
        }
        value?.documents?.forEach { document ->
            document.toObject(PurchaseItem::class.java)?.let {
                it.purchaseId = document.id
                items.add(it)
            }
        }
    }

    // Emit success state with data
    emit(State.success(items))
}.catch {
    // If exception is thrown, emit failed state along with message.
    emit(State.failed(it.message.toString()))
}.flowOn(Dispatchers.IO)
 

и в ViewModel я пробовал как:

 fun getAllItemsRealTime() = repository.getAllItemsRealTime()
 

а затем в fragment измененном loadItems() виде:

 private suspend fun loadItems(root: View, adapter: CurrentMonthAdapter) {
        currentMonthViewModel.getAllItemsRealTime().collect {
            when(it){
                is State.Loading -> {
                    showToast(root, "Loading")
                }
                is State.Success -> {
                    Log.e("__DATA__", it.data.toString())
                    adapter.submitList(it.data)
                }
                is State.Failed -> {
                    Log.e("__DATA__", it.message)
                    showToast(root, "Failed!")
                }
            }
        }

    }
 

при этом результаты остаются теми же, что и раньше. Чего не хватает в приведенном выше коде? Или есть другой способ получить данные и изменения данных в режиме реального времени? Пожалуйста, предложите решение.