Futureproof Futureproof - 4 months ago 47
Android Question

Iterating over network API call using Retrofit2

So I'm using a custom google search API to get an image URL for each name parameter in my dataset.

I'd like to iterate over each name in the dataset as a query parameter in an API call to Retrofit2.

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


So far I've built the Retrofit adapter and API instance to return an Observable:

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(" ","+"));
}


I have a getPhotos(String name) method that creates an observable and subscribes to it. I am able to run this method with a test parameter, i.e.getPhotos("rocket") and it returns a Photos object which contains a List of the top 10 google photo search results for "rocket"

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);
}
});
}


Is there a safe/correct way of iterating over this method, consequently calling the API 100's of times without running out of memory or creating 100's of observables?

For example, could I do something like this? It seems very resource intensive:

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 (Working example) resulting in a combined list of Photo objects:

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);
}
});
}
});
}

Answer

You can do something like this:

    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);
                }
            })

Create an Observable from the List. Use the map operator to replace spaces with pluses. Use flatMap to call the API. Use toList to create a list of all the responses. Then you can add ALL the responses with addAll to the adapter.