Java.lang.IllegalArgumentException: этот источник уже был добавлен другим наблюдателем

Может ли кто-нибудь правильно объяснить мне это выражение ... Кажется, это проблема, с которой я столкнулся в настоящее время.

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.

В настоящее время у меня в качестве модели ViewModel (называется 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);
        });
    }
}

В действии у меня вот что:

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 + "");
            });
        }
    }
}

Все работает так же гладко, однако, когда я выхожу из этого действия и возвращаюсь, приложение вылетает с ошибкой:

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)

Я буду очень признателен за объяснение, почему я продолжаю получать эту ошибку.

Спасибо

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
9
0
5 457
2

Ответы 2

Вы настраиваете наблюдателей 2 на том же источнике MediatorLiveData.

Вы можно установить только 1 наблюдателя на источник, иначе выдается IllegalStateException, как и в вашем случае.

Переместите свой метод observe с onResume() на onCreate().

ViewModel не разрушается, когда вы переводите свою деятельность в фоновый режим. Он все еще находится в памяти, ожидая возврата активности на передний план. Он уничтожается только тогда, когда деятельность полностью закрывается.

Если вы хотите, чтобы останавливаться наблюдал за конкретным источником, просто используйте removeSource().

Если вы хотите, чтобы Начало снова наблюдал за источником, используйте addSource().

Спасибо, @Daniel B. Моя причина размещения метода наблюдателя в onResume заключается в том, что я выполняю проверки, чтобы включить определение местоположения. Теперь, если пользователь оставляет действие на странице настроек, чтобы включить определение местоположения, он возвращается к методу onResume. А еще хотелось бы понаблюдать за Только при включении локации. Я последую вашему совету, но можете ли вы предложить способ наблюдения только при включенной локации ...? Спасибо

cr05s19xx 06.01.2019 17:48

@ cr05s19xx использует void removeSource (LiveData<S> toRemote), чтобы прекратить наблюдение за источником MediatorLiveData. Проверьте, отключена ли локация, если есть, удалите ресурс.

Daniel B. 06.01.2019 17:50

Не могли бы вы уточнить? Используя мой пример? Могу ли я вызвать addSource и removeSource в ViewModel или в действии? Спасибо

cr05s19xx 06.01.2019 18:00

@ cr05s19xx Вам необходимо хранить ваши объекты LiveData в ViewModel. Затем вам необходимо определить методы, которые позволят вам наблюдать и прекратить наблюдение, также внутри ViewModel. В вашей Activity, когда что-то происходит, и вы хотите прекратить наблюдение, вызовите метод, чтобы прекратить наблюдение из ViewModel. Когда вы будете готовы снова наблюдать, из Activity вызовите метод из ViewModel, который снова начнет наблюдение.

Daniel B. 06.01.2019 18:21

1.) вам, как правило, НИКОГДА не следует использовать onResume, если вы не работаете с камерой.

Но если да, то вам следует использовать observeForever и removeObserver вместо .observe(LifecycleOwner.

2.) Это

   // 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);
        });
    }
}

должно быть

   // Load Status will be used to fill up the progress bar inside the Activity
    private final MediatorLiveData<Integer> loadStatus = new MediatorLiveData<>();

    {
        loadStatus.addSource(healthTipRepository.createHealthTips(), healthTips -> {
            int currentValue = loadStatus.getValue();
            int newValue = currentValue != null ? currentValue + 25 : 25;
            loadStatus.setValue(newValue);
        });
    }

    @Inject
    SplashViewModel(HealthTipRepository healthTipRepository) {
        this.healthTipRepository = healthTipRepository;
    }

    // LiveData<Integer> observeLoadStatus() {
    // 
    // }
}

Другие вопросы по теме