2015-01-20 5 views
22

Я использую Retrofit + RxJava в приложении для Android и задаю себе вопрос о том, как обрабатывать разбиение на страницы API на цепочку вызовов до тех пор, пока все данные не будут восстановлены. Это что-то вроде этого:Ручка пейджинга с RxJava

Observable<ApiResponse> getResults(@Query("page") int page); 

ApiResponse объект имеет простую структуру:

class ApiResponse { 
    int current; 
    Integer next; 
    List<ResponseObject> results; 
} 

API-интерфейс будет возвращать следующий значение в пока не последняя страница.

Есть ли хороший способ достичь этого? Попробовал объединить некоторые flatMaps(), но не имел успеха.

+0

Не могли бы вы уточнить ваш вклад и ваш вывод? Подпись метода с ее описанием будет лучше. – zsxwing

ответ

41

Вы можете смоделировать рекурсивно:

Observable<ApiResponse> getPageAndNext(int page) { 
    return getResults(page) 
     .concatMap(new Func1<ApiResponse, Observable<ApiResponse>>() { 

     @Override 
     public Observable<ApiResponse> call(ApiResponse response) { 
      // Terminal case. 
      if (response.next == null) { 
      return Observable.just(response); 
      } 
      return Observable.just(response) 
       .concatWith(getPageAndNext(response.next)); 
     } 

     }); 
} 

Затем, чтобы потреблять его,

getPageAndNext(0) 
    .concatMap(new Func1<ApiResponse, Observable<ResponseObject>>() { 

     @Override 
     public Observable<ResponseObject> call(ApiResponse response) { 
      return Observable.from(response.results); 
     } 

    }) 
    .subscribe(new Action1<ResponseObject>() { /** Do something with it */ }); 

Это вы должны получить поток ResponseObject, который будет поступать в порядке, и, скорее всего, прибудет в страницу -размерные куски.

+5

Каждый день я обнаруживаю нового оператора RxJava (concatMap для меня новый). Протестировано и одобрено :) –

+1

В этом примере есть ли способ дождаться появления всех результатов, а затем объединить результаты в один ответ, на который вы можете подписаться? –

+1

'toList', вероятно, самый простой, который выдает единственный список всех элементов после завершения« Наблюдения ». есть несколько других операторов для группировки в коллекции, например, 'reduce'. – lopar

1

Я ответил на мое решение в аналогичной должности: https://stackoverflow.com/a/34378263/143733

Хитрость или поправка к решению представленной @Iopar является включение «спускового крючка», который может наблюдаемого быть испускаемого различными способами.

В коде, который я опубликовал, он выдается после обработки полной страницы элементов, однако это также может происходить на основе щелчка пользователем кнопки/прокрутки.

1

Iopar дал отличный пример.

Просто небольшое дополнение.
Если вы хотите получить все страницы в одном вызове onNext().
Это может быть полезно, если вы хотите zip этот результат с еще одним наблюдаемым.
Вы должны написать:

private List<String> list = new LinkedList() { 
    { 
     add("a"); 
     add("b"); 
     add("c"); 
    } 
}; 

int count = 1; 

public Observable<List<String>> getAllStrings(int c) { 
    return Observable.just(list) 
      .concatMap(
        strings -> { 
         if (c == 3) { 
          return Observable.just(list); 
         } else { 
          count += 1; 
          return Observable.zip(
            Observable.just(list), 
            getAllStrings(count), 
            (strings1, strings2) -> { 
             strings1.addAll(strings2); 
             return strings1; 
            } 
          ); 
         } 
        } 
      ); 
} 

Usages:

getAllStrings(0) 
     .subscribe(strings -> { 
      Log.w(TAG, "call: " + strings); 
     }); 

и вы получите:

call: [a, b, c, a, b, c, a, b, c, a, b, c] 
Смежные вопросы