当前代码:
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中的所有请求都没有结束。
答案 0 :(得分:35)
等待所有请求完成的干净利落的方法是将Retrofit2与RxJava2及其zip
函数结合使用。
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
}
答案 1 :(得分:2)
您可以通过进行同步改装调用来实现它。为了避免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);
}
这将确保第二次调用仅在第一次调用完成后发生。
如果您使用的是rx-java,则可以使用this答案中使用的Zip / flatMap运算符。
答案 2 :(得分: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 就可以了!希望它有所帮助。
P.S。也许有更简洁的方法来做到这一点。
答案 3 :(得分:0)
任何检查此问题的人。这对我有用(科特琳)
useEffect