如何发出多个请求并等待数据来自改造 2.0 中的所有请求 - android
How to make multiple request and wait until data is come from all the requests in retrofit 2.0 - android
当前代码:
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。一旦所有调用的数据都可用并添加到新闻列表中,调用
AdapterRecommendation adapter = new AdapterRecommendation(getApplicationContext(), newsList);
listView.setAdapter(adapter);
任何人都可以帮助我从多个调用中获取数据并等待所有请求在改造 2.0 中结束的最佳方法是什么。
你可以通过同步改造调用来实现。为避免 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 运算符。
如果您不介意再添加一个依赖项,您可以使用 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。也许有更清洁的方法来做到这一点。
等待所有请求完成的简洁方法是将 Retrofit2 与 RxJava2 及其 zip
函数结合使用。
zip
所做的基本上是构建新的可观察对象,它会等待所有改造 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
}
任何检查此问题的人。这对我有用 (Kotlin)
fun manyRequestsNetworkCall(requests: ArrayList<Observable<*>>, activity: Activity){
Observable.zip(requests){results ->
activity.runOnUiThread(Runnable {
//do something with those results
// runOnUiThread solves the problem cannot do something on background thread
})
// observeOn and subscribeOn solvesthe problem of NetworkOnMainThreadException
}.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.doOnSubscribe { userWorkdysResponse.value = Response.loading((requestType)) }
.subscribe ({
// do something when all the requests are done
},{
// do something if there is an error
})
}
这是一个基于 kotlin 协程的解决方案。
//turn the request methods into suspend functions
@GET("data1")
suspend fun getData(): Response<Data1>
@GET("data2")
suspend fun getData2(): Response<Data2>
//define a data class to ecapsulate data from several results
class Data{
val data1: Data1,
val data2: Data2
}
//generic class to encapsulate any request result
sealed class Result<out T : Any?> {
data class Success<out T : Any?>(val data: T) : Result<T>()
data class Error(val message: String, val exception: Exception?) : Result<Nothing>()
}
scope.launch {
val result = withContext(Dispatchers.IO) {
try {
//start two requests in parallel
val getData1Task = async { webservice.getData1() }
val getData2Task = async { webservice.getData2() }
//await for both to finish
val data1Response = getData1Task.await()
val data2Response = getData2Task.await()
//process the response
if (data1Response.isSuccessful && data2Response.isSuccessful)
Result.Success(Data(data1Response.body()!!,data2Response.body()!!))
else
Result.Error("server error message", null)
} catch (e: Exception) {
Result.Error(e.message.orEmpty(), e)
}
}
//main thread
result.run {
when (this) {
is Result.Success -> {
//update UI
}
is Result.Error -> {
toast(message)
log(message)
}
}
}
}
当前代码:
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。一旦所有调用的数据都可用并添加到新闻列表中,调用
AdapterRecommendation adapter = new AdapterRecommendation(getApplicationContext(), newsList);
listView.setAdapter(adapter);
任何人都可以帮助我从多个调用中获取数据并等待所有请求在改造 2.0 中结束的最佳方法是什么。
你可以通过同步改造调用来实现。为避免 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 运算符。
如果您不介意再添加一个依赖项,您可以使用 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。也许有更清洁的方法来做到这一点。
等待所有请求完成的简洁方法是将 Retrofit2 与 RxJava2 及其 zip
函数结合使用。
zip
所做的基本上是构建新的可观察对象,它会等待所有改造 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
}
任何检查此问题的人。这对我有用 (Kotlin)
fun manyRequestsNetworkCall(requests: ArrayList<Observable<*>>, activity: Activity){
Observable.zip(requests){results ->
activity.runOnUiThread(Runnable {
//do something with those results
// runOnUiThread solves the problem cannot do something on background thread
})
// observeOn and subscribeOn solvesthe problem of NetworkOnMainThreadException
}.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.doOnSubscribe { userWorkdysResponse.value = Response.loading((requestType)) }
.subscribe ({
// do something when all the requests are done
},{
// do something if there is an error
})
}
这是一个基于 kotlin 协程的解决方案。
//turn the request methods into suspend functions
@GET("data1")
suspend fun getData(): Response<Data1>
@GET("data2")
suspend fun getData2(): Response<Data2>
//define a data class to ecapsulate data from several results
class Data{
val data1: Data1,
val data2: Data2
}
//generic class to encapsulate any request result
sealed class Result<out T : Any?> {
data class Success<out T : Any?>(val data: T) : Result<T>()
data class Error(val message: String, val exception: Exception?) : Result<Nothing>()
}
scope.launch {
val result = withContext(Dispatchers.IO) {
try {
//start two requests in parallel
val getData1Task = async { webservice.getData1() }
val getData2Task = async { webservice.getData2() }
//await for both to finish
val data1Response = getData1Task.await()
val data2Response = getData2Task.await()
//process the response
if (data1Response.isSuccessful && data2Response.isSuccessful)
Result.Success(Data(data1Response.body()!!,data2Response.body()!!))
else
Result.Error("server error message", null)
} catch (e: Exception) {
Result.Error(e.message.orEmpty(), e)
}
}
//main thread
result.run {
when (this) {
is Result.Success -> {
//update UI
}
is Result.Error -> {
toast(message)
log(message)
}
}
}
}