現在のコード:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constant.BASEURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
APIService service = retrofit.create(APIService.class);
Call<ResponseWrap> call = service.getNewsData();
call.enqueue(new Callback<ResponseWrap>() {
@Override
public void onResponse(Call<ResponseWrap> call1, Response<ResponseWrap> response) {
if (response.isSuccess()) {
ResponseWrap finalRes = response.body();
for(int i=0; i<finalRes.getResponse().getResults().size(); ++i){
String title = finalRes.getResponse().getResults().get(i).getWebTitle();
News n = new News(titleCategory, title, null);
newsList.add(n);
}
AdapterRecommendation adapter = new AdapterRecommendation(getApplicationContext(), newsList);
listView.setAdapter(adapter);
}
else{
Toast.makeText(getApplicationContext(), "onResponse - something wrong" + response.message(), Toast.LENGTH_LONG).show();
}
}
@Override
public void onFailure(Call<ResponseWrap> call1, Throwable t) {
Toast.makeText(getApplicationContext(), "exception: " + t.getMessage(), Toast.LENGTH_LONG).show();
}
});
正常に動作します。
今、私は複数の呼び出しを行いたいです(呼び出しの数は実行時に決定されます)、すべての呼び出しは同じ形式のデータを提供します。すべての呼び出しからのデータをnewsListに追加する必要があります。すべての呼び出しからデータが利用可能になり、newsListに追加されたら、呼び出します
AdapterRecommendation adapter = new AdapterRecommendation(getApplicationContext(), newsList);
listView.setAdapter(adapter);
誰もが複数の呼び出しからデータを取得し、すべての要求がレトロフィット2.0で終了するまで待機するための最良の方法を教えてもらえますか。
すべてのリクエストが完了するまで待機する、クリーンできれいなアプローチは、RxJava2およびそのZip
関数と組み合わせてRetrofit2を使用することです。
Zip
が行うことは、基本的に新しいobservableを構築し、すべてのレトロフィットObservable
要求が完了するまで待機してから、独自の結果を出力します。
Observablesを使用したRetrofit2インターフェイスの例を次に示します。
public interface MyBackendAPI {
@GET("users/{user}")
Observable<User> getUser(@Path("user") String user);
@GET("users/{user}/photos")
Observable<List<Photo>> listPhotos(@Path("user") String user);
@GET("users/{user}/friends")
Observable<List<User>> listFriends(@Path("user") String user);
}
複数のリクエストを作成し、それらすべてが完了した後にのみ別の処理を行うコードでは、次のように記述できます。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.build();
MyBackendAPI backendApi = retrofit.create(MyBackendAPI.class);
List<Observable<?>> requests = new ArrayList<>();
// Make a collection of all requests you need to call at once, there can be any number of requests, not only 3. You can have 2 or 5, or 100.
requests.add(backendApi.getUser("someUserId"));
requests.add(backendApi.listPhotos("someUserId"));
requests.add(backendApi.listFriends("someUserId"));
// Zip all requests with the Function, which will receive the results.
Observable.Zip(
requests,
new Function<Object[], Object>() {
@Override
public Object apply(Object[] objects) throws Exception {
// Objects[] is an array of combined results of completed requests
// do something with those results and emit new event
return new Object();
}
})
// After all requests had been performed the next observer will receive the Object, returned from Function
.subscribe(
// Will be triggered if all requests will end successfully (4xx and 5xx also are successful requests too)
new Consumer<Object>() {
@Override
public void accept(Object o) throws Exception {
//Do something on successful completion of all requests
}
},
// Will be triggered if any error during requests will happen
new Consumer<Throwable>() {
@Override
public void accept(Throwable e) throws Exception {
//Do something on error completion of requests
}
}
);
それで全部です :)
Kotlin
で同じコードがどのように見えるかを示したい場合に備えて。
val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.build()
val backendApi = retrofit.create(MyBackendAPI::class.Java)
val requests = ArrayList<Observable<*>>()
requests.add(backendApi.getUser())
requests.add(backendApi.listPhotos())
requests.add(backendApi.listFriends())
Observable
.Zip(requests) {
// do something with those results and emit new event
Any() // <-- Here we emit just new empty Object(), but you can emit anything
}
// Will be triggered if all requests will end successfully (4xx and 5xx also are successful requests too)
.subscribe({
//Do something on successful completion of all requests
}) {
//Do something on error completion of requests
}
同期レトロフィット呼び出しを行うことでそれを達成できます。 NetworkOnUiExceptionを回避するために、asynctask内でこれを実行しています。
List<Something> list = new ArrayList();
public void doInBackground(){
for(int i = 0; i < numberOfCalls; i++){
Call<Something> call = service.method1("some_value");
List<Something> list = call1.execute().body();
list.add(list1);
}
}
public void onPostExecute(){
AdapterRecommendation adapter = new AdapterRecommendation(getApplicationContext(), newsList);
listView.setAdapter(adapter);
}
これにより、最初の呼び出しが完了した後にのみ2番目の呼び出しが行われます。
Rx-Javaを使用している場合、 this answerで使用されているZip/flatMap演算子を使用できます。
依存関係をもう1つ追加してもかまわない場合は、 RxAndroid を使用できます。特に、次のようなサービスインターフェイスを変更する必要があります。
@GET("/data")
Observable<ResponseWrap> getNewsData();
今、あなたはこれを行うことができます:
Observable
.range(0, **numberOfTimes**, Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.doOnError(new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
Log.e("error", throwable.toString());
}
})
.concatMap(new Func1<Integer, Observable<ResponsWrapper>>() {
@Override
public Observable<ResponsWrapper> call(Integer integer) {
Log.i("news", "nr:" + integer);
//Does the call.
return service.getNewsData(integer);
}
}).concatMap(new Func1<ResponsWrapper, Observable<News>>() {
@Override
public Observable<News> call(final ResponsWrapper responsWrapper) {
return Observable.fromCallable(new Func0<News>() {
@Override
public News call() {
//change the result of the call to a news.
return new News(responsWrapper.category,responsWrapper.title,null);
}
});
}
}).toList().subscribe(new Action1<List<News>>() {
@Override
public void call(List<News> newList) {
AdapterRecommendation adapter = new AdapterRecommendation(getApplicationContext(), newsList);
listView.setAdapter(adapter);
}
});
numberOfTimesを変更するだけで機能します!それが役に立てば幸い。
追伸これを行うには、よりクリーンな方法があるかもしれません。