简体   繁体   中英

Send data from repository to view model in MVVM architecture

I'm confused about using MVVM architecture, In some tutorials, LiveData is stored in a repository and then passed to the ViewModel . In many others, LiveData is defined within a function of repository and passed to the ViewModel using the result of the function. But Google says :

It may be tempting to work LiveData objects in your data layer class, but LiveDatais not designed to handle asynchronous streams of data. ........ If you need to use streams of data in other layers of your app, consider using Kotlin Flows and then converting them to LiveData in the ViewModel using asLiveData(). .... For codebases built with Java, consider using Executors in conjuction with callbacks or RxJava.

I prefer to use Kotlin coroutines or Flows . But I do not know if in my case is really needed or not. I'm working on a chat application. When a message is received, a listener is called in the repository and the message data is received in it. (The listener runs in the background thread) Now I want to send the message object to the ViewModel and add it to a LiveData that stores the list of messages.

object ChatsRepo {

fun listen(socket: Socket) {
    socket.on(Config.ON_MESSAGE, onMessage)
}

fun sendMessage(socket: Socket, json: String) {
    socket.emit(Config.ON_MESSAGE, json)
}

private val onMessage = Emitter.Listener { args: Array<Any> ->
    //This message object must be sent to ViewModel
    val message = Gson().fromJson(args[0].toString(), Message::class.java)
}
}

I can easily do this using the higher-order function:

object ChatsRepo {

lateinit var listener: (Message) -> Unit

private val onMessage = Emitter.Listener { args: Array<Any> ->
    val message = Gson().fromJson(args[0].toString(), Message::class.java)
    listener(message)
}
}

But is it better to use Kotlin coroutines or Flows ? In some similar cases, a list needs to be sent to the ViewModel .

I can easily do this using the higher-order function.

Right, this is a callback you can use to notify ViewModel about new messages. Kotlin Coroutines help to avoid callbacks and to have a sequential code.

In your case the onMessage is a hot stream of data, we can convert it to a hot Flow using SharedFlow :

private val _messagesFlow = MutableSharedFlow<Message>(extraBufferCapacity = 64)
val messagesFlow: SharedFlow<Message> = _messagesFlow

private val onMessage = Emitter.Listener { args: Array<Any> ->
    val message = Gson().fromJson(args[0].toString(), Message::class.java)
    messagesFlow.tryEmit(message)
}

In ViewModel if need it is easy to convert it to LiveData using method asLiveData :

ChatsRepo.messagesFlow.asLiveData()

Dependency to use asLiveData() extension function:

def lifecycle_version = "2.4.0"

implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"

If you are a Rxjava master, I will not recommend you to use flow/livedata. I think flow/livedata is designed for UI Reactive, not for underlying data transform. So you can use rxjava in data repository, and in viewmodel, you can convert it to livedata and use it.

Livedata was never design for reactive streams it was always been for last layer (viewmodel to view) , there were/are workaround when livedata was use with retrofit(using calladapter) and room(which google did it).
And now since the rise of coroutines recommended ways is to use kotlin Flows when dealing with business logic but things to be notice:
1- regular flow are not observables but livedata is observable.
2- it's not good to manage UI state with regular flows but you can do it with livedata .
3- livedata is lifecycle aware but flows are not.
4- livedata is not reactive but flows are.
5- livedata gives you the only latest value what it receives but regular flows throws values from bucket one by one

you might have notice i said alot of regular flows not flow cause there are other mechanism related to flows you can manage UI state that are stateFlow and sharedFlow .
Stateflow is the replacement of livedata the downside is you have to code a little bit more to make it lifecycle aware cause it is not prebuilt and stateflow always gives you the last value it persists during the configuration changes or screen switching(fragment navigation).
sharedFlows is good for managing the one time event like toastMsgs, snackbar etc. sharedFlow was actually the replacement of BroadcastChannels .

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