LiveData не запускается дважды в случае сбоя сетевого запроса - PullRequest
0 голосов
/ 07 сентября 2018

Мой LiveData объект вызывается дважды, когда мой httprequest выполнен успешно, но в случае ошибки он вызывается только один раз, в результате чего пользовательский интерфейс отображает пустой список, поскольку моя проверка Error была пропущена, так как не было предупреждения

Код моего репозитория

private LiveData<List<Card>> getCards() {
    return cardsDao.getCards();
}

// Network request, that deals with the success or error of the request

public LiveData<DataWrapper<List<Card>>> loadCards() {
    AtomicReference<LiveData<DataWrapper<List<Card>>>> atomicWrapper = new AtomicReference<>();
    if (getCards().getValue() == null) {
        webService.getCards(result -> {
            CardResponse res = (CardResponse) result;
            List<Card> cardList = res.getCardsList();
            getInsertAsyncTask.execute(cardList.toArray(new Card[cardList.size()]));
        }, error -> {
            atomicWrapper.set(asLiveData(null, error.getMessage()));
        }, TAG);
    }
    atomicWrapper.set(asLiveData(getCards(),null));
    return atomicWrapper.get();
}

Код моего базового репозитория

LiveData<DataWrapper<List<T>>> asLiveData(LiveData<List<T>> dataSource, String error) {
    MediatorLiveData<DataWrapper<List<T>>> mediatorLiveData = new MediatorLiveData<>();
    mediatorLiveData.addSource(dataSource, data -> mediatorLiveData.setValue(new DataWrapper<>(data, error)));
    return mediatorLiveData;
}

Код моего фрагмента

private void subscribeToCards() {
    mViewModel.getCards().observe(this, listDataWrapper -> {
        if( listDataWrapper == null ) {
            return;
        }

        if( listDataWrapper.error != null) {
            // Show error on UI
            dismissProgress();
            Log.e(TAG, "Error - " + listDataWrapper.error);
            showError(getString(R.string.cards_error_get_list_message));
            EventBus.getDefault().post(new DialogMessageEvent(getString(R.string.cards_error_get_list_title),
                getString(R.string.cards_error_get_list_message), getString(R.string.cards_error_get_list_button)));
        }

        if( listDataWrapper.data != null ) {
            // Update Ui
            refreshCardsList(listDataWrapper.data);
            cardsViewVisibility(true);
        }
    });
}

И, наконец, мой код ViewModel

public LiveData<DataWrapper<List<Card>>> getCards(){
    return repository.loadCards();
}

Подводя итог, в случае сбоя, почему observer callback вызывается только один раз?Поскольку я отлаживал его, и в обоих случаях (успешном и неудачном) метод asLiveData называется TWICE, но только в успешной попытке обратный вызов называется TWICE aswell, а при ошибке observer callback он вызывается толькоОДИН РАЗ.

Редактировать: добавлен код асинхронной задачи

@SuppressLint("StaticFieldLeak")
AsyncTask<T,Void,Void> getInsertAsyncTask = new AsyncTask<T, Void, Void>() {
    @Override
    protected Void doInBackground(T... ts) {
        daoObject.insertObjects(Arrays.asList(ts));
        return null;
    }
};

1 Ответ

0 голосов
/ 07 сентября 2018

Причина, по которой вы получаете 2 обратных вызова для случая успеха и только 1 вызов для сценария ошибки, похоже, связана с настройкой вашего репозитория.

При вызове loadCards вы сначала выводите состояние базы данных с помощью этого вызова:

atomicWrapper.set(asLiveData(getCards(),null));

На данный момент ваша база данных будет запрошена, и текущие значения вызовут mediatorLiveData.setValue. Это будет первый выброс.

mediatorLiveData.addSource(dataSource, data -> mediatorLiveData.setValue(new DataWrapper<>(data, error)));

В то же время вы инициировали вызов веб-службы, которая в случае успеха вызовет вашу асинхронную задачу для обновления базы данных.

webService.getCards(result -> {
    CardResponse res = (CardResponse) result;
    List<Card> cardList = res.getCardsList();
    getInsertAsyncTask.execute(cardList.toArray(new Card[cardList.size()]));
}, error -> {
    atomicWrapper.set(asLiveData(null, error.getMessage()));
}, TAG);

Как только команда вставки завершается, MediatorLiveData снова инициирует вызов setValue - он прослушивает изменения в базе данных, поэтому при вставке он получает обратный вызов. Это секунда эмиссия в случае успеха.

В сценарии ошибки вы передаете null в качестве источника данных. Удивительно, что это не дает сбоя, поскольку метод addSource помечает параметр источника как Non Null . Поскольку это значение равно нулю, вы не получаете обратного вызова, и mediatorLiveData.setValue не будет вызываться. Это означает, что для сценария ошибки вы получаете только первое излучение.

Может быть проще, если вы сделали что-то вроде следующего:

  • настроить прослушиватель базы данных и выдать ваше значение данных без ошибок при обновлении базы данных.

  • при получении ошибки вы можете просто выдать DataWrapper с ошибкой

например. что-то вроде:

    private final AtomicBoolean isLoading = new AtomicBoolean(false);
    private final MediatorLiveData<DataWrapper<List<Card>>> mediatorLiveData = new MediatorLiveData<>();

    private MyRepository() {
        // requires a cardsDao
        // listen for DB changes and emit on callback
        mediatorLiveData.addSource(cardsDao.getCards(), data -> mediatorLiveData.setValue(new DataWrapper<>(data, null)));
    }

    public LiveData<DataWrapper<List<Card>>> cardsData() {
        return mediatorLiveData;
    }

    public void loadCards() {
        if (!isLoading.get()) {
            isLoading.set(true);
            webService.getCards(result -> {
                CardResponse res = (CardResponse) result;
                List<Card> cardList = res.getCardsList();
                // Trigger update in db
                getInsertAsyncTask.execute(cardList.toArray(new Card[cardList.size()]));
                isLoading.set(false);
            }, error -> {
                // Emit the error
                mediatorLiveData.setValue(new DataWrapper<>(null, error.getMessage()));
                isLoading.set(false);
            }, TAG);
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...