#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!")
}
}
}
}
при этом результаты остаются теми же, что и раньше. Чего не хватает в приведенном выше коде? Или есть другой способ получить данные и изменения данных в режиме реального времени? Пожалуйста, предложите решение.