Can anyone please explain this expression properly for me... It seems to be the problem I'm facing currently.
MediatorLiveData#addSource
Starts to listen the given source LiveData, onChanged observer will be called when source value was changed.
onChanged
callback will be called only when this MediatorLiveData is active.If the given LiveData is already added as a source but with a different Observer,
IllegalArgumentException
will be thrown.
I currently have the following as my ViewModel (Called SplashViewModel
)
package com.testapp.testapp.ui.splash;
import com.testapp.testapp.repository.HealthTipRepository;
import javax.inject.Inject;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;
import androidx.lifecycle.ViewModel;
public class SplashViewModel extends ViewModel {
private final HealthTipRepository healthTipRepository;
// Load Status will be used to fill up the progress bar inside the Activity
private final MediatorLiveData<Integer> loadStatus = new MediatorLiveData<>();
@Inject
SplashViewModel(HealthTipRepository healthTipRepository) {
this.healthTipRepository = healthTipRepository;
}
LiveData<Integer> observeLoadStatus() {
loadStatus.addSource(healthTipRepository.createHealthTips(), healthTips -> {
int currentValue = loadStatus.getValue();
int newValue = currentValue != null ? currentValue + 25 : 25;
loadStatus.setValue(newValue);
});
}
}
In the activity, I have this:
package com.testapp.testapp.ui.splash;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.os.Bundle;
import android.provider.Settings;
import android.view.View;
import android.widget.ProgressBar;
import com.testapp.testapp.R;
import com.testapp.testapp.storage.PrefManager;
import com.testapp.testapp.ui.BaseActivity;
import com.testapp.testapp.ui.FactoryViewModel;
import javax.inject.Inject;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProviders;
import butterknife.BindView;
// Base Activity has already injected the Dagger component
public class SplashActivity extends BaseActivity {
@BindView(R.id.splash_progress)
ProgressBar progressBar;
@Inject
FactoryViewModel factoryViewModel;
private SplashViewModel viewModel;
// PrefManager is responsible for managing shared preferences. It exposes a .get Method
@Inject
PrefManager prefManager;
@Override
protected void onCreate(@Nullable Bundle savedInstance) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash_activity);
ButterKnife.bind(this);
viewModel = ViewModelProviders.of(this, factoryViewModel).get(SplashViewModel.class);
}
@Override
protected void onResume() {
super.onResume();
// Performs checks to turn on location. The viewmodel is placed in the
// onREsume to ensure that even when we leave the activity to turn on the
// location and return, we can always start the viewmodel
boolean turnedOnLocation = false;
if (!turnedOnLocation) {
startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
}
boolean appSetup = prefManager.get("app_setup", false);
if (!appSetup) {
viewModel.observeLoadStatus().observe(this, status -> {
progressBar.setProgress(status + "");
});
}
}
}
Everything runs as smoothly, however, when I leave this activity and return, the application crashes with the error:
Process: com.testapp.testapp, PID: 29865
java.lang.RuntimeException: Unable to resume activity {com.testapp.testapp/com.testapp.testapp.ui.splash.SplashActivity}: java.lang.IllegalArgumentException: This source was already added with the different observer
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3609)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3649)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1663)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6524)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:888)
Caused by: java.lang.IllegalArgumentException: This source was already added with the different observer
at androidx.lifecycle.MediatorLiveData.addSource(MediatorLiveData.java:89)
at com.testapp.testapp.ui.splash.SplashViewModel.fetchSensorLocations(SplashViewModel.java:25)
at com.testapp.testapp.ui.splash.SplashViewModel.observeLoadStatus(SplashViewModel.java:17)
at com.testapp.testapp.ui.splash.SplashActivity.observeViewModels(SplashActivity.java:121)
at com.testapp.testapp.ui.splash.SplashActivity.onResume(SplashActivity.java:77)
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1355)
at android.app.Activity.performResume(Activity.java:7138)
I will really appreciate an explanation and why I keep getting this error.
Thanks
You are setting up 2 observers on the same source of the MediatorLiveData
.
You can only set 1 observer per source, otherwise an IllegalStateException
is thrown, just like in your case.
Move your observe
method from onResume()
to onCreate()
.
A ViewModel does not get destroyed when you are putting your activity in the background. It is still in memory, waiting for the activity to return to the foreground. It only gets destroyed when the activity is completely closed.
If you want to stop observing a particular source, simply use removeSource()
.
If you want to start observing the source again, use addSource()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With