Can we use LiveData without loosing any value? - android

I would like to use a LiveData for handling kind of notifications, as it is already lifecycle aware, between a custom view and its wrapping fragment. But it seems that a LiveData may loose values : it will only update to its most recent state and also won't fire values during inactive state of its observers.
I've looked at the SingleLiveEvent purpose from Google code samples, but that solution does not seems to be battle tested yet, and the ticket is still open with recent tries to improve the solution.
So I am looking for a simple way to get notified about events, and at the same time not being worried about Lifecycles (that was why I went for LiveData as a first solution), and that could handle multiple observers.
Is there an existing solution for that ? If I try to implement it, it is sure that I will land into at least an anti-pattern.
One easy way (perhaps too easy) is to use callbacks : but the problem is that I need this feature for several callbacks in my component, leading me in a poor architecture. And also, I want a subscribe system, meaning that there could be more than one observer.
One other way, could be to use RxJava and tranform it into a LiveData, with LiveDataReactiveStreams.fromPublisher() : but now the question is whether I will get all values or only the last one. That's the closest solution I could deal with.
As an interesting alternative there could be AutoDispose or RxLifecycle. And an interesting resource I've found : Blog post on LiveData
What are your thoughts, suggestions ?
Also, please notice that I need this communication from a component wrapped into a Fragment (ChessBoard) toward another Fragment (ChessHistory). So they are both lifecycle aware.

It is not ideal, but this does the trick for me:
/**
* This LiveData will deliver values even when they are
* posted very quickly one after another.
*/
class ValueKeeperLiveData<T> : MutableLiveData<T>() {
private val queuedValues: Queue<T> = LinkedList<T>()
#Synchronized
override fun postValue(value: T) {
// We queue the value to ensure it is delivered
// even if several ones are posted right after.
// Then we call the base, which will eventually
// call setValue().
queuedValues.offer(value)
super.postValue(value)
}
#MainThread
#Synchronized
override fun setValue(value: T) {
// We first try to remove the value from the queue just
// in case this line was reached from postValue(),
// otherwise we will have it duplicated in the queue.
queuedValues.remove(value)
// We queue the new value and finally deliver the
// entire queue of values to the observers.
queuedValues.offer(value)
while (!queuedValues.isEmpty())
super.setValue(queuedValues.poll())
}
}
The main problem with this solution is that if the observers are inactive at the time the values are delivered via super.setValue(), then the values will be lost regardless. However, it solves the issue of losing values when several new ones are posted very quickly – which, in my opinion, is usually a bigger problem than losing values because your observer is inactive. After all, you can always do myLiveData.observeForever() from a non-lifecycle-aware object in order to receive all notifications.
Not sure this will be enough for you, but I hope it can help you or give you some ideas about how to implement your own approach.

Related

Can I modify the data set of a PagingDataAdapter using peek()?

I am looking for a way to update specific items in my PagingDataAdapter from the Paging 3 library. The recommended way at the moment seems to be to invalidate the PagingSource but this causes the adapter to fetch the whole data set again, which is not efficient and also shows my loading spinner.
However, I noticed that I can access and modify items in the adapter using the peek() method and it seems to work quite well. Am I missing anything here? Will this fall apart in certain scenarios? I know that it's good practice to keep data classes immutable but this approach makes my life a lot easier.
Here is an example of my usage and it seems to work quite well:
viewModel.chatMessageUpdateEvents.collect { messageEvent ->
when (messageEvent) {
is FirestoreChatMessageListener.ChatMessageUpdateEvent.MessageUpdate -> {
val update = messageEvent.chatMessage
val historyAdapterItems = chatMessagesHistoryAdapter.snapshot().items
val updatedMessage =
historyAdapterItems.find { chatMessage ->
chatMessage.documentId == messageEvent.chatMessage.documentId
}
if (updatedMessage != null) {
val messagePosition = historyAdapterItems.indexOf(updatedMessage)
chatMessagesHistoryAdapter.peek(messagePosition)?.unsent = update.unsent
chatMessagesHistoryAdapter.peek(messagePosition)?.imageUrl = update.imageUrl
chatMessagesHistoryAdapter.notifyItemChanged(messagePosition)
}
}
}
}
I replied in a separate comment but wanted to post here for visibility.
This is really not recommended and a completely unsupported usage of paging.
One of the primary ways of restoring state if Pager().flow() hasn't been cleaned up (say if ViewModel hasn't been cleared yet) is via the .cachedIn(scope) method, which will cache out-of-date data in your case. This is also the only way to multicast (make the loaded data in PagingData re-usable) for usage in Flow operations like .combine() that allow you to mix transformations with external signals.
You'll also need to handle races between in-flight loads, what happens if you get a messageEvent the same time an append finishes? Who wins in this case and is it possible between taking the .snapshot() a new page is inserted so your notify position is no longer correct?
In general it's much simpler to have a single source of truth and this is the recommended path, so the advice has always been to invalidate on every backing dataset update.
There is an open FR in Paging's issue tracker to support Flow<Item> or Flow<Page> style data to allow granular updates, but it's certainly a farther future thing: https://issuetracker.google.com/160232968

Observable transformers made of Subjects

I am currently implementing a pattern that has a view-viewmodel circular dependency. Though its really not dependency because they don't know about each other, all they know is that there is a stream of events and a stream of states. I came up with an idea of making the viewModel implement a function called toTransformer() which returns an ObservableTransformer that's composed of two subjects, an event subject and a state subject.
private val eventStream: PublishSubject<MainEvent> = PublishSubject.create()
private val stateSink: BehaviorSubject<MainState> = BehaviorSubject.create()
...
fun asTransformer(): ObservableTransformer<MainEvent, MainState> =
ObservableTransformer {
it.subscribe { eventStream.onNext(it) }
stateSink
}
And is used like this
view.events().compose(viewModel.asTransformer()).subscribe { view.render(it) }
Questions
Is it okay to do this?
What could go wrong with this implementation.
Will the inner subscription be disposed if the subscription is disposed?
Can this be improved to a better form?
Edit
This is how event and state relates.
eventStream.map { it.toAction() }
.compose(actionToResult())
.scan (MainState.initial(), reducer())
.subscribe {
stateSink.onNext(it)
}
I don't know if you based it off of this, but Jake Wharton has a great presentation on this kind of architecture.
Is it okay to do this?
In general, sure.
What could go wrong with this implementation.
One thing you probably want to be careful of is that you essentially have one big event loop. If your event loop dies, the UI will be non-responsive. Correct error handling is even more important than before. I'm sure your code snippets above are a simplified version of what you really have, but consider that without an error handling block, failures in your inner subscription will bubble up to your outer subscription which will itself fail. At this point, there will be no active subscriptions to UI events.
Will the inner subscription be disposed if the subscription is disposed?
No. It's not in the same chain.
Can this be improved to a better form?
Especially in consideration of the previous answer, you may want to get rid of the inner subscription so that it's all one chain. An easy way is to use flatMap instead of subscribing.

Naming convention for methods returning RxJava's Completable

I have and Android app with the view class (Fragment, Activity) observing its ViewModel.
The ViewModel exposes methods such as getUserName which returns Observable<String>. Although maybe there is a possibility to find a better name (maybe observeUserName), I'm happy with the current one - it is quite explanatory.
However, here starts the hard part: ViewModel also can tell the view to perform some operation - for example close itself, pop backstack etc. For this case ViewModel defines following method (and corresponging Subject):
class ViewModel {
// other methods, fields
// ViewModel can call returnToPreviousScreen.onComplete()
CompletableSubject returnToPreviousScreen = CompletableSubject.create();
Completable returnToPreviousScreen() { return returnToPreviousScreen; }
}
In my opinion, the method's name is terrible. Hovewer I can not find anything better. Something like observeWhenToReturnToPreviousScreen is maybe more explanatory but hard to read.
So, are there any recommendations or maybe commonly used practices for naming such methods?
There's no universal answer to the naming problem, so the only thing you can get are opinions.
Rule of thumb
My approach to naming in rx-java usually looks at two things:
Does it express a "stream" of emitted events (usually with a plural form of a noun)?
Does it work well with other parts of rx java methods chain, and especially the subscribe method?
Both of the above can be usually simplified to trying to put the name of the method in this sentence:
This code subscribes to {name_of_the_method}.
Examples
A) getUserName
This code subscribes to getUserName.
👎 The sentence does not really make sense because getUserName does not express the stream. Quite on the contrary, it suggests that there is one value that you can get.
getUserName().subscribe()
B) observeUserName
This code subscribes to observeUserName.
👎 Although the method kind-of expresses the stream of events, it does not work well with subscribe. Method exposing the Observable is not a place for information about observing. The consumer of the method will be observing what that method returns.
observeUserName().subscribe()
C) userNames
This code subscribes to userNames.
👎👍 This might work in some cases. It nicely expresses a stream of userName items being emitted and works well with subscribe. It really depends on a particular scenario, because it suggests that you can expect multiple userNames while you really want to observe how a single userName changes.
userNames().subscribe()
C) userNameChanges
This code subscribes to userNameChanges.
👍 This method nicely expresses that there is a stream of items ("change" events) and it works well with subscribe method.
userNameChanges().subscribe()
Return to previous screen
As far as your returnToPreviousScreen case goes, I think I would end up using something like:
This code subscribes to returnRequests().
or
This code subscribes to previousScreenRequests().
or even a singular form as there can only be one event emitted in the stream:
This code subscribes to previousScreenRequest().
(not a topic of a question but I think I would use a Single<Unit> rather than Completable, to express a singular event emission rather than a completion... but maybe that's just me).

Trigger Observable on Subject's onNext and Share Result

I have a button which when pressed should make the btnSubject's onNext fire and make an API call in an Observable created in my ViewModel like so:
val apiObservable =
btnSubject.flatMap{ apiService.getSomething()
.toResponseOrErrorObservable()
.subscribeOn(Schedulers.io())}
Then I can reuse this observable to create two more, which are then subscribed to from my view allowing me to keep the logic in my ViewModel like so:
val successObservable = apiObservable.filter{ it.isSuccess() }
val failureObservable = apiObservable.filter{ it.isFailure() }
So apiObservable is triggered by the btnSubject.onNext() being called.
The view is then updated because it's listening to the successObservable and failureObservable
Is this possible? Perhaps with a .share() on the apiObservable?
UPDATE
I added the share operator and all observables emitted items when first subscribing. Even the filters didn't stop it... I must be missing something obvious
There might be a few way to do that.
As you have written, using share() operator multiplies output to many Subscribers. However, you have to be careful, that you also have to call connect() to turn cold Observable into hot one. If calling also replay(), you woudln't need to call connect() many times.
(Source)
However, there is more simple solution: use Jake Wharton's library RxReplayingShare. The author of previous blog post suggests it in his next article

Why should one consider using AndroidObservables in RxJava

As i understand AndroidObservable helps ensure that :
a Subscriber always observes on the main thread
when a fragment/activity is detached/stopped, then the observation stops immediately, and framework related components (like ui textviews etc.) are not updated.
However, in order to ensure that the context is released (preventing leakage), most examples I see typically say that you have to anyway do an .unsubscribe onDestroyView/onDestroy, which essentially halts the subscription, and prevents the subscriber from receiving these updates anyway.
So my question is:
Is there any other advantage to using AndroidObservables, if i manually indicate that the subscription should happen on the main thread, by way of .observeOn(AndroidSchedulers.mainThread() ?
Is there any difference in the below two approaches?
_subscription1 = AndroidObservable.bindFragment(MyFragment.this, myCustomAwesomeObservable()) //
.subscribeOn(Schedulers.io()) //
.subscribe(...);
_subscription2 = myCustomAwesomeObservable()
.subscribeOn(Schedulers.io()) //
.observeOn(AndroidSchedulers.mainThread()) //
.subscribe(...);
#Override
public void onDestroyView() {
_subscription1.unsubscribe();
_subscription2.unsubscribe();
super.onDestroyView();
}
You are right. What AndroidObservable.bindFragment currently does is:
This helper will schedule the given sequence to be observed on the main UI thread and ensure that no notifications will be forwarded to the activity in case it is scheduled to finish.
-- part of the source code comment
So, it does not really make a difference which of the implementations you use.
But, still it's a good idea to use the AndroidObservable as additional functionality could be added in the future.
It doesn't exist anymore since 1.0 release of RxAndroid. I guess you could say it's deprecated or discontinued. I don't think it's a good idea to use this anymore.

Categories

Resources