2016-12-07 3 views
1

나는 RxJava에 들어가려고하고 있으므로 그것에 관한 게시물을 읽었습니다. 어떻게 작동하는지 이해했다고 생각하지만 라이브러리에 대한 올바른 이해를 돕기 위해 이론 코드를 제출하고 싶습니다.RxJava를 사용하여 개량 호출 연결하기

생산자 목록과 각 제작자, 앨범 및 노래를 검색 할 수있는 API가 있다고 가정 해 보겠습니다. 내 모델이 될 것

public class Produceur { 
    private Integer id; 
    private String name; 
    private String pictureUrl; 
} 

public class Artist { 
    private Integer id; 
    private String name; 
    private String lastname; 
    private String sceneName; 
    private String pictureUrl; 
} 

public class Album { 
    private Integer id; 
    private int year; 
    private String name; 
    private String style; 
    private String pictureUrl; 
    private Integer artistId; 
} 

public class Song { 
    private Integer id; 
    private String title; 
    private int duration; 
    private String pictureUrl; 
    private Integer albumId; 
} 
내 Retrofist 서비스

@GET("myUrl/produceurs") 
Observable<List<ProduceurResponse>> getProduceurs(); 

@GET("myUrl/produceurs/{produceurId}") 
Observable<List<ArtisteResponse>> getArtistForProduceur(@Path("produceurId") Integer produceurId); 

@GET("myUrl/picture/{id}") 
Observable<ResponseBody> getPicture(@Path("id") Integer id); 

그리고 응답이

public class ProduceurResponse { 
    private Integer id; 
    private String name; 
    private String pictureUrl; 
} 

public class ArtisteResponse { 
    private Integer id; 
    private String name; 
    private String lastname; 
    private String sceneName; 
    private String pictureUrl; 
    private List<AlbumResponse> albums; 
} 

public class AlbumResponse { 
    private Integer id; 
    private int year; 
    private String name; 
    private String style; 
    private String pictureUrl; 
    private Integer artistId; 
    private List<SongResponse> songs; 
} 

public class SongResponse { 
    private Integer id; 
    private String title; 
    private int duration; 
    private String pictureUrl; 
    private Integer albumId; 
} 

내가 모든 produceur를 검색하고자하는 객체 각각에 대해 예술가를 다음과 동일한 순서로 로컬 메모리의 모든 이미지를 저장하십시오. 각 프로듀서마다 아티스트, 앨범 및 노래를 검색하여 우리베이스에 삽입 할 다음 코드에 대해 생각했습니다. 일단 제작자와 아티스트를 검색하고 나면 이미지를 다운로드 할 수 있습니다 (사진이있는 모든 ID의 목록에 저장).

getProduceurs().flatMap(produceurs -> Observable.from(produceurs)) 
       .doOnNext(produceur -> insertProduceurInBase(produceur)) 
       .subscribe(produceur -> Observable.from(getArtistForProduceur(produceur.getId()) 
               .flatMap(artists -> Observable.from(artists)) 
               .doOnNext(artist -> insertArtistInBase(artist))) 
               .subscribe(), 

          e -> showError(e), 

         () -> Observable.from(listOfIdsToDownload) 
              .doOnNext(id -> getPicture(id)) 
              .subscribe(response -> createImage(response), 

              e -> showError(e), 

             () -> isFinished() 
             ) 
         ); 

이 코드가 작동합니까 (필자는 확실하지만 확실하지 않습니다)? 이것을하는 것이 최선의 방법입니까?

내 getProduceurs 서비스가 프로듀서 아티스트 ID가 포함 된 ProduceurResponse 목록을 반환하면 아티스트 프로필을 가져오고 다른 프로필을 가져 와서 앨범을 가져 오는 서비스가 제공됩니다.

public class ProduceurResponse { 
    private Integer id; 
    private String name; 
    private String pictureUrl; 
    List<Integer> artistsIds; 
} 

public class ArtisteProfileResponse { 
    private Integer id; 
    private String name; 
    private String lastname; 
    private String sceneName; 
    private String pictureUrl; 
} 

@GET("myUrl/artist/{artistId}") 
Observable<List<ArtisteProfileResponse>> getArtistProfile(@Path("artistId") Integer artistId); 

@GET("myUrl/artist/{artistId}/detail") 
Observable<List<AlbumResponse>> getArtistAlbums(@Path("artistId") Integer artistId); 

나는)합니다 (getArtistProfile()와 getArtistAlbums을

getProduceurs().flatMap(produceurs -> Observable.from(produceurs)) 
       .doOnNext(produceur -> insertProduceurInBase(produceur)) 
       .subscribe(produceur -> Observable.from(produceur.getArtistIds()) 
               .zip(
                 getArtistProfile(), 
                 getArtistAlbums(), 
                 (artistProfil, albumList) -> insertArtistInBase() 
               ) 
               .subscribe(), 

          e -> showError(e), 

         () -> Observable.from(listOfIdsToDownload) 
              .doOnNext(id -> getPicture(id)) 
              .subscribe(response -> createImage(response), 

              e -> showError(e), 

             () -> isFinished() 
             ) 
         ); 

처럼 뭔가 동시 호출 한 .zip을 사용하는 수도 있지만 정말 우편을 사용하고 잘 모르겠어요 맞아. zip은이 코드 조각에서 잘 사용됩니까? 이게 효과가 있니? 이것을하는 것이 최선의 방법입니까?

편집은 그래서 구글 도서 API를 내 원래의 아이디어 비슷한을 구현하기 위해 노력했다.

나는

public class BookSearchResult { 
    public List<BookResult> items; 
} 

public class BookResult { 
    public String id; 
    public String selfLink; 
    public VolumeInfoResult volumeInfo; 
    public SaleInfoResult saleInfo; 
} 

으로 개조 인터페이스

public interface IBookService { 
    @GET("volumes?q=robot+subject:fiction") 
    Observable<BookSearchResult> getFictionAuthors(@Query("category") String key); 
    @GET("volumes") 
    Observable<BookSearchResult> getBooksForAuthor(@Query("q") String author, @Query("category") String key); 
} 

있어 그리고 난 나에게를 포함하는 BookSearchResult을 반환 가진 캐릭터 로봇 (getFictionAuthors)과 소설 책을 검색하려고 BookResult의 목록. 각 도서 결과에 대해 getBooksForAuthor를 사용하여 저자의 모든 도서를 검색합니다. 내 코드는

Observable<BookResult> observable = mWebService.getFictionAuthors(API_KEY) 
       .flatMap(new Func1<BookSearchResult, Observable<BookResult>>() { 

        // Parse the result and build a CurrentWeather object. 
        @Override 
        public Observable<BookResult> call(final BookSearchResult data) { 
         return Observable.from(data.items); 
        } 
       }) 
       .concatMap(new Func1<BookResult, Observable<BookSearchResult>>() { 

        // Parse the result and build a CurrentWeather object. 
        @Override 
        public Observable<BookSearchResult> call(final BookResult data) { 
         return mWebService.getBooksForAuthor("=inauthor:" + data.volumeInfo.authors.get(0), API_KEY); 
        } 
       }) 
       .flatMapIterable(new Func1<BookSearchResult, List<BookResult>>() { 

        // Parse the result and build a CurrentWeather object. 
        @Override 
        public List<BookResult> call(final BookSearchResult data) { 
         return data.items; 
        } 
       }) 
       .subscribeOn(Schedulers.newThread()) 
       .observeOn(AndroidSchedulers.mainThread()) 
       .subscribe(new Subscriber<BookResult>() { 
        @Override 
        public void onNext(final BookResult book) { 
         Log.e("Book","Book is " + book.volumeInfo.title + " written by " + book.volumeInfo.authors.get(0)); 
        } 

        @Override 
        public void onCompleted() { 
         Log.e("Book","Book list completed"); 
        } 

        @Override 
        public void onError(final Throwable error) { 
         Log.e("Book","Book list error"); 
        } 
       } 

입니다.이 코드는 작동하지만 이해가 안가는 이상한 것이 있습니다. 내 기록에는 처음에는 getBooksForAuthor 요청에서 첫 번째 저자의 요청을받은 다음 해당 저자의 각 책에서 얻은 로그가 있습니다. 그 후 두 번째 저자에 대한 요청의 결과와 그의 서적에서 나온 로그의 일부를 얻었습니다.다른 저자의 요청 결과와 두 번째 저자의 도서 목록 끝과 다른 모든 저자의 도서 목록을 따르십시오. 내가 기대하는 경우

을 설명하기 위해, 내 로그

- > Return from request for Author 1 
- > Book 1 from author 1 
... 
- > Book 10 from author 1 
- > Return from request for Author 2 
- > Book 1 from author 2 
... 
- > Book 5 from author 2 
- > Return from request for Author 3 
- > Return from request for Author 4 
... 
- > Return from request for Author 10 
- > Book 6 from author 2 
.. 
- > Book 10 from author 2 
- > Book 1 from author 3 
.. 
- > Book 10 from author 3 
... 
- > Book 1 from author 10 
.. 
- > Book 10 from author 10 

과 같은

- > Return from request for Author 1 
- > Book 1 from author 1 
... 
- > Book 10 from author 1 
- > Return from request for Author 2 
- > Book 1 from author 2 
... 
- > Book 10 from author 2 
... 
- > Return from request for Author 10 
- > Book 1 from author 10 
... 
- > Book 10 from author 10 

누군가가 설명을하거나 내가 부족 이해합니까?

+0

은 바로 시퀀스에서 이미지를 저장하려면 하시겠습니까? –

+0

모든 데이터를 검색하고 이미지를 동일한 순서로 저장하고 싶습니다. 내가 기억하고있는 것은 내가 로그인하면 내 앱이 필요한 모든 데이터를 검색 한 다음 앱을 사용할 수 있다는 것입니다. – Tibo

+0

rxjava에는 다음 연산자가 있습니다. merge() - api에서받은 데이터를 차례대로 가져옵니다. buffer() :이 연산자는로드가 없도록 버퍼를 만듭니다. 당신은 이것을 시도 할 수 있습니다 –

답변

1

중첩 구독은 피해야합니다 (flatmap 연산자 참조). 이것은 코드 냄새의 지표가됩니다. 중첩 구독을 방지하려면 flatMap 연산자 (결과 이벤트 순서를 garanti하지 않음) 또는 concatMap (garanti 결과 이벤트 순서) 연산자를 사용할 수 있습니다.

완성 된 콜백에서 다른 Observable을 사용하고 있는데이 경우에는 concat 관측 값을 사용할 수 있습니다.

그래서 운영자의 이런 종류 사용하여 코드의 재 : 정확히 당신이 원하는

getProduceurs().flatMap(produceurs -> Observable.from(produceurs)) 
       .doOnNext(produceur -> insertProduceurInBase(produceur)) 
       // call getArtistForProduceur and emit results in order 
       .concatMap(produceur -> getArtistForProduceur(produceur.getId())) 
       // emits items of the list 
       .flatMapIterable(artists -> artists) 
       .doOnNext(artist -> insertArtistInBase(artist))) 
       // don't care about elements. But will wait for the completion of the previous observable 
       .ignoreElements() 
       // perform jobs after the previous observable complete 
       .concatWith(Observable.from(listOfIdsToDownload) 
             .doOnNext(id -> getPicture(id)) 
             .doOnNext(response -> createImage(response))) 
       // show an error if an error occur in the downstream 
       .doOnError(e -> showError(e)) 
       // call isFinished when everything is finished. 
       .doOnCompleted(() -> isFinished()) 
       .subscribe() 
+0

대단히 감사합니다. 나는 concatMap에 대해 몰랐습니다. 나는 여전히 평면적이다. (예술가 -> 예술가) flatMap (예술가 -> 관측 가능하다. 예술가)과 같은 질문이있다. – Tibo

+0

예. 참조 : http://reactivex.io/RxJava/javadoc/rx/Observable.html#flatMapIterable(rx.functions.Func1) – dwursteisen

+0

도움 주셔서 감사합니다! – Tibo

관련 문제