简体   繁体   中英

Return data from Repository to ViewModel without LiveData

I'm just trying to find an answer how to pass the data from Repository to ViewModel without extra dependencies like RxJava . The LiveData seems as a not good solution here because I don't need to proceed it in my Presentation , only in ViewModel and it's not a good practice to use observeForever . The code is simple: I use Firebase example trying to pass data with Flow but can't use it within a listener ( Suspension functions can be called only within coroutine body error):

Repository

    fun fetchFirebaseFlow(): Flow<List<MyData>?> = flow {
        var ret: List<MyData>? = null
        firebaseDb.child("data").addListenerForSingleValueEvent(
            object : ValueEventListener {
                override fun onDataChange(dataSnapshot: DataSnapshot) {
                    val data = dataSnapshot.getValue<List<MyData>>()
                    emit(data) // Error. How to return the data here?
                }

                override fun onCancelled(databaseError: DatabaseError) {
                    emit(databaseError) // Error. How to return the data here?
                }
            })
//        emit(ret) // Useless here
    }

ViewModel

    private suspend fun fetchFirebase() {
        repo.fetchFirebaseFlow().collect { data ->
            if (!data.isNullOrEmpty()) {
                // Add data to something
            } else {
                // Something else
            }
    }

You can use callbackFlow

@ExperimentalCoroutinesApi
    fun fetchFirebaseFlow(): Flow<List<String>?> = callbackFlow {
        
        val listener = object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                val data = dataSnapshot.getValue<List<MyData>>()
                offer(data)
            }

            override fun onCancelled(databaseError: DatabaseError) {
                
            }
        }
        val ref =firebaseDb.child("data")
        reef.addListenerForSingleValueEvent(listener)

        awaitClose{
            //remove listener here
           ref.removeEventListener(listener)
        }
    }

ObservableField is like LiveData but not lifecycle-aware and may be used instead of creating an Observable object.

{
    val data = repo.getObservable()

    val cb = object : Observable.OnPropertyChangedCallback() {
        override fun onPropertyChanged(observable: Observable, i: Int) {
            observable.removeOnPropertyChangedCallback(this)
            val neededData = (observable as ObservableField<*>).get()
        }
    }

    data.addOnPropertyChangedCallback(cb)
}


fun getObservable(): ObservableField<List<MyData>> {
    val ret = ObservableField<List<MyData>>()
    firebaseDb.child("events").addListenerForSingleValueEvent(
        object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                ret.set(dataSnapshot.getValue<List<MyData>>())
            }

            override fun onCancelled(databaseError: DatabaseError) {
                ret.set(null)
            }
        })
    return ret
}

It is also possible to use suspendCancellableCoroutine for a single result. Thanks to Kotlin forum .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM