#android #observable #retrofit2 #rx-java2 #rx-android
Вопрос:
У меня есть функция, которая принимает список идентификаторов статей для установки на адаптере. Все работает нормально, пока хотя бы один из запросов не завершится неудачей. Тогда возвращенный список пуст. Как заставить его игнорировать неудачный запрос и перейти к следующему? Например, я запрашиваю 5 статей, 1 из которых не работает, 4 в порядке, поэтому я получаю список из 4.
Я знаю, что мне нужно использовать onErrorResumeNext()
здесь, но я не знаю, как.
Интерфейс:
@GET("articles/{id}") Observablelt;Articlesgt; getArticle1(@Path("id") int id);
Активность:
private void getMoreArticles(Listlt;Integergt; l) { ApiInterface apiInterface = ApiClient.getApiClientRX().create(ApiInterface.class); Listlt;Observablelt;?gt;gt; requests = new ArrayListlt;gt;(); for (int id : l) { requests.add(apiInterface.getArticle1(id)); } Observable.zip(requests, new Functionlt;Object[], Listlt;Articlesgt;gt;() { @Override public Listlt;Articlesgt; apply(@NonNull Object[] objects) { Listlt;Articlesgt; articlesArrayList = new ArrayListlt;gt;(); for (Object response : objects) { articlesArrayList.add((Articles) response); } return articlesArrayList; } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .onErrorResumeNext(Observable.lt;Listlt;Articlesgt;gt;empty()) .subscribe( new Consumerlt;Listlt;Articlesgt;gt;() { @Override public void accept(Listlt;Articlesgt; articlesList) { adapter = new Adapter(articlesList, MainActivity.this); if (fav) recyclerView.setAdapter(adapter); else addRV().setAdapter(adapter); adapter.notifyDataSetChanged(); initListener(); swipeRefreshLayout.setRefreshing(false); } }, new Consumerlt;Throwablegt;() { @Override public void accept(Throwable e) throws Exception { } } ).isDisposed(); }
Ответ №1:
Я попытался немного упростить ваш вариант использования, но, надеюсь, вы поняли мою точку зрения. Вам нужно каким-то образом «просигнализировать», что в вашем вызове API возникла какая-то проблема, и этот конкретный Articles
объект должен быть пропущен в .zip()
функции молнии вашего оператора. Например, вы можете обернуть возвращаемое значение в Optional
. Когда значение задано, это означает, что все прошло нормально. Если нет, вызов API не удался.
class SO69737581 { private Observablelt;Articlesgt; getArticle1(int id) { return Observable.just(new Articles(id)) .map(articles -gt; { if (articles.id == 2) { // 1. throw new RuntimeException("Invalid id"); } else { return articles; } }); } Observablelt;Listlt;Articlesgt;gt; getMoreArticles(Listlt;Integergt; ids) { Listlt;Observablelt;Optionallt;Articlesgt;gt;gt; requests = new ArrayListlt;gt;(); for (int id : ids) { Observablelt;Optionallt;Articlesgt;gt; articleRequest = getArticle1(id) .map(article -gt; Optional.of(article)) // 2. .onErrorReturnItem(Optional.empty()); // 3. requests.add(articleRequest); } return Observable.zip(requests, objects -gt; { Listlt;Articlesgt; articlesArrayList = new ArrayListlt;gt;(); for (Object response : objects) { Optionallt;Articlesgt; optionalArticles = (Optionallt;Articlesgt;) response; optionalArticles.ifPresent(articlesArrayList::add); // 4. } return articlesArrayList; }); } }
Объяснение интересных деталей:
- Имитация ошибки API с идентификатором = 2
- Оберните результат API-интерфейса вызова в необязательный
- При возникновении ошибки возвращайте пустой необязательный параметр
- Добавьте значение статьи в массив результатов, если это значение присутствует
Проверка:
public class SO69737581Test { @Test public void failedArticleCallsShouldBeSkipped() { SO69737581 tested = new SO69737581(); TestObserverlt;Listlt;Articlesgt;gt; testSubscriber = tested .getMoreArticles(Arrays.asList(1, 2, 3, 4)) .test(); Listlt;Articlesgt; result = Arrays.asList( new Articles(1), new Articles(3), new Articles(4) ); testSubscriber.assertComplete(); testSubscriber.assertValue(result); } }
Для полноты картины, вот как я определил Article
класс:
class Articles { public int id; public Articles(int id) { this.id = id; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Articles articles = (Articles) o; return id == articles.id; } @Override public int hashCode() { return Objects.hash(id); } }