如何批量发起Retrofit2 Call并统一回调?多图上传如何改为单次请求?
嘿,我来帮你搞定这两个Retrofit2的实战问题,都是日常开发里很常见的场景:
问题1:如何发起多个Retrofit2 Call请求,所有请求完成后执行统一回调
这里有几种靠谱的实现方式,按推荐程度排序:
方式1:用RxJava(最简洁推荐)
Retrofit支持RxJava适配器,借助Rx的操作符可以轻松实现批量请求的统一回调。比如用Single.zip(需要获取每个请求的结果)或者Completable.mergeArray(只需要确认所有请求完成,不需要结果)。
步骤:
- 先添加RxJava相关依赖(确保你的Retrofit版本适配):
implementation 'io.reactivex.rxjava3:rxjava:3.1.5' implementation 'io.reactivex.rxjava3:rxandroid:3.0.2' implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
- 修改Retrofit接口,让方法返回
Single/Observable而不是Call:
public interface ApiService { @GET("data1") Single<DataModel1> fetchData1(); @GET("data2") Single<DataModel2> fetchData2(); }
- 创建Retrofit实例时添加RxJava适配器:
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://your-api-url.com/") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) .build(); ApiService apiService = retrofit.create(ApiService.class);
- 合并多个请求并统一处理:
// 如果需要获取每个请求的结果,用Single.zip Single.zip( apiService.fetchData1(), apiService.fetchData2(), (data1, data2) -> new CombinedResult(data1, data2) // 封装多请求结果 ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( combinedResult -> handleSuccess(combinedResult), // 所有请求成功完成 throwable -> handleError(throwable) // 任意请求失败触发 ); // 如果只需要确认所有请求完成,不需要结果,用Completable.mergeArray Completable.mergeArray( apiService.fetchData1().ignoreElement(), apiService.fetchData2().ignoreElement() ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( this::handleAllComplete, // 全部完成回调 this::handleError // 任意失败回调 );
方式2:用Java CompletableFuture(无Rx依赖)
如果不想引入RxJava,Java 8+的CompletableFuture也能实现:
步骤:
- 把Retrofit的Call转换成CompletableFuture:
private <T> CompletableFuture<T> callToFuture(Call<T> call) { CompletableFuture<T> future = new CompletableFuture<>(); call.enqueue(new Callback<T>() { @Override public void onResponse(Call<T> call, Response<T> response) { if (response.isSuccessful()) { future.complete(response.body()); } else { future.completeExceptionally(new IOException("Request failed: " + response.code())); } } @Override public void onFailure(Call<T> call, Throwable t) { future.completeExceptionally(t); } }); return future; }
- 批量执行并等待全部完成:
ApiService apiService = retrofit.create(ApiService.class); CompletableFuture<DataModel1> future1 = callToFuture(apiService.fetchData1Call()); CompletableFuture<DataModel2> future2 = callToFuture(apiService.fetchData2Call()); // 等待所有任务完成 CompletableFuture.allOf(future1, future2) .thenApply(v -> new CombinedResult(future1.join(), future2.join())) .whenComplete((result, throwable) -> { if (throwable != null) { handleError(throwable); } else { handleSuccess(result); } });
方式3:手动管理计数器(不推荐,易出错)
如果以上两种都不想用,也可以手动维护一个计数器,每个请求完成后递增,达到总数时触发回调:
private int completedCount = 0; private static final int TOTAL_REQUESTS = 2; private CombinedResult combinedResult = new CombinedResult(); // 发起第一个请求 apiService.fetchData1Call().enqueue(new Callback<DataModel1>() { @Override public void onResponse(Call<DataModel1> call, Response<DataModel1> response) { if (response.isSuccessful()) combinedResult.setData1(response.body()); checkIfAllCompleted(); } @Override public void onFailure(Call<DataModel1> call, Throwable t) { handleError(t); } }); // 发起第二个请求 apiService.fetchData2Call().enqueue(new Callback<DataModel2>() { @Override public void onResponse(Call<DataModel2> call, Response<DataModel2> response) { if (response.isSuccessful()) combinedResult.setData2(response.body()); checkIfAllCompleted(); } @Override public void onFailure(Call<DataModel2> call, Throwable t) { handleError(t); } }); // 检查是否所有请求完成 private void checkIfAllCompleted() { completedCount++; if (completedCount == TOTAL_REQUESTS) { handleSuccess(combinedResult); } }
这种方式代码冗余,且失败场景需要额外处理,尽量不用。
问题2:将多张图片独立上传改为单次请求批量上传
这个核心是利用Retrofit的**Multipart(多部分表单)**请求,一次请求里携带多个文件。
步骤:
- 修改Retrofit接口,定义支持多文件上传的Multipart请求:
public interface ApiService { // 方式2:动态数量的文件,用@PartMap(更灵活,推荐) @Multipart @POST("upload/multiple") Call<UploadResponse> uploadMultipleImages(@PartMap Map<String, MultipartBody.Part> images); }
- 构建Multipart请求体,把所有图片转换成
MultipartBody.Part:
// 假设你有一个图片文件列表List<File> imageFiles Map<String, MultipartBody.Part> imagePartMap = new HashMap<>(); for (int i = 0; i < imageFiles.size(); i++) { File file = imageFiles.get(i); // 创建请求体 RequestBody requestBody = RequestBody.create(file, MediaType.parse("image/jpeg")); // 构建MultipartBody.Part,参数对应服务器接收的字段名、文件名、请求体 MultipartBody.Part imagePart = MultipartBody.Part.createFormData( "files", // 服务器端对应的字段名,比如Spring Boot用@RequestParam("files") file.getName(), requestBody ); imagePartMap.put("file" + (i+1), imagePart); }
- 发起单次请求并处理回调:
ApiService apiService = retrofit.create(ApiService.class); Call<UploadResponse> call = apiService.uploadMultipleImages(imagePartMap); call.enqueue(new Callback<UploadResponse>() { @Override public void onResponse(Call<UploadResponse> call, Response<UploadResponse> response) { if (response.isSuccessful()) { handleUploadSuccess(response.body()); } else { handleUploadError(response.message()); } } @Override public void onFailure(Call<UploadResponse> call, Throwable t) { handleUploadError(t.getMessage()); } });
注意事项:
- 确保服务器端支持接收多个文件,比如Spring Boot后端可以这样写:
@PostMapping("/upload/multiple") public ResponseEntity<UploadResponse> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) { // 处理多个文件逻辑 }
- 注意配置客户端和服务器的文件大小阈值,避免因文件过大导致上传失败。
内容的提问来源于stack exchange,提问作者Riyas Ahamed
相关产品推荐
相关产品推荐

