RxJava2 не выполняет цепочку в архитектуре MVVM - PullRequest
0 голосов
/ 30 октября 2018

У нас есть следующий код в LoginViewModel:

private MutableLiveData<Response> loginLiveData;

@Inject
public LoginViewModel(LoginUseCase loginUseCase) {
    loginLiveData = new MutableLiveData<>();
    this.loginUseCase = loginUseCase;
}
@Override
public void onAttached() {
    checkHasToken();
}

public void checkHasToken() {
    add(loginUseCase.hasToken()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnComplete(() -> Timber.d("doOnComplete"))
            .doFinally(() -> Timber.d("doFinally"))
            .doOnDispose(() -> Timber.d("doOnDispose"))
            .doOnTerminate(() -> Timber.d("doOnTerminate"))
            .doOnError(Timber::d)
            .doOnSubscribe(disposable -> Timber.d("doOnSubscribe " + disposable.isDisposed()))
            .subscribe(
                    () -> {
                        loginLiveData.setValue(Response.success());
                        Timber.d("Subscribe completable");
                    },
                    t -> {
                        loginLiveData.setValue(Response.error(t));
                        Timber.d("Subscribe error");
                    }
            )
);
}

hasToken() просто проверяет, существует ли токен внутри SharedPreferences:

public Completable hasToken() {
    return !TextUtils.isEmpty(rawToken()) ? Completable.complete() : Completable.error(new Throwable("TokenIsEmpty");
}

И код, который успешно выполняется, как показано в журналах:

D/LoginViewModel: doOnSubscribe false
D/LoginViewModel: doOnComplete
D/LoginViewModel: doOnTerminate
D/LoginViewModel: Subscribe completable
D/LoginViewModel: doFinally

Тем не менее, изредка ничего не выполняется, и журналы выглядят следующим образом:

D/LoginViewModel: doOnSubscribe false

И ЭТО ВСЕ! Экран застрял на этом месте, и не следует его дальнейшая логика. Это также происходит на других экранах.

Моя база ViewModel выглядит так:

public abstract class AbsViewModel extends AndroidViewModel {

private static CompositeDisposable disposables;

public AbsViewModel(@NonNull Application application) {
    super(application);
}
public AbsViewModel() {
    super(null);
}

public void onAttached() {}

@Override
public void onCleared() {
    dispose();
    super.onCleared();
}

public static void add(Disposable disposable) {
    getCompositeDisposable().add(disposable);
}

protected void dispose() {
    disposables.dispose();
    disposables.clear();
}

public static void dispose() {
    Timber.d("dispose");
    getCompositeDisposable().dispose();
}

private static CompositeDisposable getCompositeDisposable() {
    if (disposables == null || disposables.isDisposed()) {
        disposables = new CompositeDisposable();
    }
    return disposables;
}

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

1 Ответ

0 голосов
/ 30 октября 2018

У вас есть static CompositeDisposable disposables в AbsViewModel, что является причиной этого странного поведения. Это не очень хорошая идея, потому что все экземпляры ViewModel имеют доступ к одному и тому же экземпляру disposables - могут dispose() и воссоздать его.

Давайте рассмотрим пример: у нас есть 2 экрана с отдельным ViewModel (расширен от AbsViewModel). Когда вы переключаетесь с первого экрана на второй и возвращаетесь к первому экрану, вы получите следующий след:

  1. Экран1 ViewModel.attach()
  2. Экран2 ViewModel.attach()
  3. Экран1 ViewModel.attach()
  4. Screen1 ViewModel.onCleared(), который запускает disposables.dispose()

В этом случае на шаге (3) вы воссоздадите disposables и добавите новый, но сразу же на следующем шаге (4) вы утилизируете его.

Это только один из возможных сценариев, который указывает на проблему static расходных материалов, общих для всех экземпляров AbsViewModel. Если вы удалите модификатор static из disposables и все методы, которые с ним работают - ваша проблема исчезнет.

...