2016-07-13 5 views
2

So verwende ich ein benutzerdefiniertes Google-Such-API, um eine Bild-URL für jeden Namensparameter in meinem Dataset zu erhalten.Iterieren über Netzwerk-API-Aufruf mit Retrofit2

Ich möchte über jeden Namen im Dataset als Abfrageparameter in einem API-Aufruf von Retrofit2 iterieren.

public interface GoogleImageAPI { 
    @GET("v1?key=MyAPIKEY&cx=MyCustomSearchID&searchType=image&fileType=jpg&imgSize=large&alt=json") 
    Observable<Photos> loadPhotos(@Query("q") String artistQuery); 
} 

Bisher habe ich die Retrofit-Adapter und API-Instanz aufgebaut haben einen beobachtbaren zurückzukehren:

public Observable<Photos> getPhotosObservable(String artistName){ 
    Retrofit retrofit = new Retrofit.Builder() 
      .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 
      .addConverterFactory(GsonConverterFactory.create()) 
      .baseUrl("https://www.googleapis.com/customsearch/") 
      .build(); 
    GoogleImageAPI googleImageAPI = retrofit.create(GoogleImageAPI.class); 
    return googleImageAPI.loadPhotos(artistName.replace(" ","+")); 
} 

Ich habe einen getPhotos (String name) -Methode, die eine beobachtbare und abonniert hat es erstellt. Ich bin in der Lage diese Methode mit einem Testparameter zu laufen, iegetPhotos („Rakete“), und es gibt ein Foto Objekt, das eine Liste der Top 10 Google-Foto Suchergebnisse für „rocket“ enthält

public void getPhotos(String artistName){ 
    Observable<Photos> photosObservable = mDataManager.getPhotosObservable(artistName); 
    Subscription subscription = photosObservable.subscribeOn(Schedulers.io()) 
     .observeOn(AndroidSchedulers.mainThread()) 
     .subscribe(new Subscriber<Photos>() { 
      @Override 
      public void onCompleted() { 

      } 

      @Override 
      public void onError(Throwable e) { 
       if(e instanceof HttpException){ 
        HttpException response = (HttpException)e; 
        Log.i(TAG,"bad response: "+response.code()); 
       } 
      } 

      @Override 
      public void onNext(Photos photos) { 
       Photos mPhotos = photos; 
       Log.i(TAG,"size: "+mPhotos); 
      } 
     }); 
} 

Gibt es eine sichere/korrekte Art, über diese Methode zu iterieren und folglich die APIs 100 mal aufzurufen, ohne den Speicher zu leeren oder 100 Observable zu erzeugen?

Zum Beispiel könnte ich so etwas tun? Es scheint sehr ressourcenintensiv:

for(String name : artistNames){ 
    googleImageAPI.loadPhotos(name.replace(" ","+")) 
     .subscribeOn(Schedulers.newThread()) 
     .observeOn(AndroidSchedulers.mainThread()) 
     .subscribe(new Subscriber<Github>() { 
     @Override 
     public final void onCompleted() { 
      // do nothing 
     } 

     @Override 
     public final void onError(Throwable e) { 
      Log.e("GithubDemo", e.getMessage()); 
     } 

     @Override 
     public final void onNext(Github response) { 
      mCardAdapter.addData(response); 
     } 
    }); 
} 

Edit (Arbeitsbeispiel), die sich in einer kombinierten Liste der Foto-Objekte:

public void getPhotos(){ 
    mDataManager.getArtists() 
     .subscribeOn(Schedulers.io()) 
     .subscribe(new Action1<JSONArray>() { 
      @Override 
      public void call(JSONArray jsonArray) { 
       LinkedHashMap<Integer,Artist> artistMap = presentArtists(jsonArray); 
       Collection<Artist> artists = artistMap.values(); 
       Observable.from(artists) 
        .map(new Func1<Artist, String>() { 
         @Override 
         public String call(Artist artist) { 
          String artistName; 
          if(artist.getName().contains("(")){ 
           artistName = artist.getName().substring(0,artist.getName().indexOf("(")-2).replace(" ","+"); 
          }else { 
           artistName = artist.getName().replace(" ", "+"); 
          } 
          return artistName; 
         } 
        }) 
        .flatMap(new Func1<String, Observable<Photos>>() { 
         @Override 
         public Observable<Photos> call(String artistName) { 
          return mDataManager.getPhotosObservable(artistName); 
         } 
        }) 
        .toList() 
        .subscribeOn(Schedulers.io()) 
        .subscribe(new Subscriber<List<Photos>>() { 
         @Override 
         public void onCompleted() {} 

         @Override 
         public void onError(Throwable e) { 

         } 

         @Override 
         public void onNext(List<Photos> objects) { 
          mDataManager.savePhotos(objects); 
         } 
        }); 
      } 
     }); 
} 

Antwort

3

Sie können etwas tun:

Observable.from(artistNames) 
      .map(name -> name.replace(" ", "+")) 
      .flatMap(name -> googleImageAPI.loadPhotos(name)) 
      .toList() 
      .subscribe(new Subscriber<List<Github>>() { 
       @Override 
       public final void onCompleted() { 
        // do nothing 
       } 

       @Override 
       public final void onError(Throwable e) { 
        Log.e("GithubDemo", e.getMessage()); 
       } 

       @Override 
       public final void onNext(List<Github> response) { 
        mCardAdapter.addAll(response); 
       } 
      }) 

Erstellen Sie eine Observable aus der List. Verwenden Sie den Operator map, um Leerzeichen durch Pluszeichen zu ersetzen. Verwenden Sie flatMap, um die API aufzurufen. Verwenden Sie toList, um eine Liste aller Antworten zu erstellen. Dann können Sie ALLE Antworten mit addAll dem Adapter hinzufügen.

+0

Das ist ausgezeichnet, danke. Ich habe eine Arbeitslösung (ohne Lambda) an meine Frage angehängt. Es ist leicht zu sehen, wie Lambda den Code viel sauberer und einfacher zu lesen macht, aber das ist eine Lektion für einen anderen Tag. – Futureproof

+1

@Futureproof mein Vergnügen. Sehen Sie sich auch https://developer.android.com/reference/java/net/URLEncoder.html für URL-Codierungszeichenfolgen an. – LordRaydenMK

+0

Noch besser! Und zu denken, ich hätte fast eine Utility-Klasse erstellt, um alle verschiedenen URL-Variationen zu behandeln. Danke noch einmal. – Futureproof