Reputation: 5074
I have such issue:
Two fragments: A and B, which inject viewModel, but for some reason I have result of LiveData in both of my fragments. How can I avoid triggering live data pushing me old value? How to reset liveData somehow or ignore old values? Thanks. In both of them I am listening to LiveData changes like this:
@AndroidEntryPoint
class LoginFragment : MyBaseDebugFragment(R.layout.spinner_layout) {
@Inject
private val viewModel: AuthorizationViewModel by activityViewModels()
{
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
viewModel.loginActionLD.observe(viewLifecycleOwner) { loginStatus ->
//Do something regarding the value of login status.
}
viewModel.DoLogin()
}
@HiltViewModel
class AuthorizationViewModel @Inject constructor(
private val login: LoginUseCase,
private val logout: LogoutUseCase,
@Dispatcher.IO private val ioDispatcher: CoroutineDispatcher,
@Scope.Application private val externalScope: CoroutineScope,
) : ViewModel() {
private val _loginActionLD = MutableLiveData<LoginAction>()
val loginActionLD: LiveData<LoginAction> = _loginActionLD
fun DoLogin(from: AuthRequest) {
launchOnMyExternalScope {
_loginActionLD.postValue(login().toLoginAction(from))
}
}
private fun launchOnMyExternalScope(block: suspend CoroutineScope.() -> Unit) =
externalScope.launch(ioDispatcher, block = block)
}
}
@Module
@InstallIn(SingletonComponent::class)
object CoroutineScopeModule {
@Singleton
@Scope.Application
@Provides
fun provideApplicationScope(@Dispatcher.IO ioDispatcher: CoroutineDispatcher): CoroutineScope =
CoroutineScope(SupervisorJob() + ioDispatcher)
}
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class Scope {
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class Application
}
Upvotes: 0
Views: 551
Reputation: 5074
Here is what helped me to avoid LiveData to trigger twice it's handler. This code is tested carefully:
open class LiveEvent<T> : MediatorLiveData<T>() {
private val observers = ArraySet<OneTimeObserver<in T>>()
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
val wrapper = OneTimeObserver(observer)
observers.add(wrapper)
super.observe(owner, wrapper)
}
@MainThread
override fun observeForever(observer: Observer<in T>) {
val wrapper = OneTimeObserver(observer)
observers.add(wrapper)
super.observeForever(wrapper)
}
@MainThread
override fun removeObserver(observer: Observer<in T>) {
if ((observer is OneTimeObserver && observers.remove(observer)) || observers.removeIf { it.observer == observer }) {
super.removeObserver(observer)
}
}
@MainThread
override fun setValue(t: T?) {
observers.forEach { it.newValue() }
super.setValue(t)
}
private class OneTimeObserver<T>(val observer: Observer<T>) : Observer<T> {
private var handled = AtomicBoolean(true)
override fun onChanged(t: T?) {
if (handled.compareAndSet(false, true)) observer.onChanged(t)
}
fun newValue() {
handled.set(false)
}
}
}
And then instead of such code:
private val _loginAction = MutableLiveData<LoginAction>()
val loginActionLD: LiveData<LoginAction> = _loginAction
I have used this code:
private val _loginAction = LiveEvent<LoginAction>()
val loginActionLD: LiveData<LoginAction> = _loginAction
Upvotes: 0
Reputation: 30735
There is a handy class SingleLiveEvent
that you can use instead of LiveData
in your ViewModel
class to send only new updates after subscription.
class SingleLiveEvent<T> : MutableLiveData<T>() {
private val pending = AtomicBoolean(false)
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
super.observe(owner, Observer<T> { t ->
if (pending.compareAndSet(true, false)) {
observer.onChanged(t)
}
})
}
override fun setValue(t: T?) {
pending.set(true)
super.setValue(t)
}
fun call() {
postValue(null)
}
}
This LiveData
extension only calls the observable if there's an explicit call to setValue()
or call()
.
Upvotes: 1