点击返回按钮后执行多HTTP请求,待成功响应后再执行返回操作的实现方法
实现方案:多请求完成后再返回页面
要实现点击BackButton时先发起多个异步HTTP请求,等待全部成功后再返回页面,我们可以按View-Presenter分层协作的思路来实现,核心是在Presenter中管理多请求的状态,待所有请求成功后通知View执行返回操作。
核心思路
- View层触发事件:点击BackButton时,不直接返回,而是调用Presenter的处理方法。
- Presenter管理多请求:并行发起所有HTTP请求,跟踪每个请求的成功状态;当所有请求都成功时,通知View返回。
- View执行返回:接收Presenter的通知后,执行页面返回(如
finish()或Navigation组件的回退)。
方案一:使用RxJava(推荐,适合已有RxJava的项目)
RxJava的zip操作符可以完美合并多个Observable,等待所有请求完成后统一处理结果。
1. 定义契约接口(DetailsScreenContract.java)
先定义View和Presenter的交互契约,确保分层清晰:
public interface DetailsScreenContract { interface View { void navigateBack(); // 执行页面返回 void showRequestError(String errorMsg); // 请求失败时提示用户 } interface Presenter { void onBackButtonClicked(); // 处理BackButton点击事件 void attachView(View view); // 绑定View void detachView(); // 解绑View,防止内存泄漏 } }
2. Presenter实现(DetailsScreenPresenter.java)
在Presenter中发起多请求,用zip合并结果:
public class DetailsScreenPresenter implements DetailsScreenContract.Presenter { private DetailsScreenContract.View mView; private CompositeDisposable mDisposable = new CompositeDisposable(); private ApiService mApiService; // 假设这是你的API请求服务类 public DetailsScreenPresenter(ApiService apiService) { mApiService = apiService; } @Override public void attachView(DetailsScreenContract.View view) { mView = view; } @Override public void detachView() { mView = null; mDisposable.dispose(); // 取消所有未完成的请求,避免内存泄漏 } @Override public void onBackButtonClicked() { // 1. 定义多个请求Observable Observable<ResponseBody> request1 = mApiService.makeFirstRequest() .subscribeOn(Schedulers.io()); Observable<ResponseBody> request2 = mApiService.makeSecondRequest() .subscribeOn(Schedulers.io()); Observable<ResponseBody> request3 = mApiService.makeThirdRequest() .subscribeOn(Schedulers.io()); // 2. 使用zip合并所有请求,等待全部成功 Disposable disposable = Observable.zip(request1, request2, request3, (resp1, resp2, resp3) -> { // 验证每个请求是否成功 if (!resp1.contentType().toString().contains("success") || !resp2.contentType().toString().contains("success") || !resp3.contentType().toString().contains("success")) { throw new RuntimeException("部分请求失败"); } return true; }) .observeOn(AndroidSchedulers.mainThread()) .subscribe( success -> { if (mView != null) { mView.navigateBack(); // 所有请求成功,通知返回 } }, error -> { if (mView != null) { mView.showRequestError("请求失败:" + error.getMessage()); } } ); mDisposable.add(disposable); } }
3. View层实现(DetailsScreen.java)
在Activity中绑定Presenter,处理BackButton点击:
public class DetailsScreen extends AppCompatActivity implements DetailsScreenContract.View { private DetailsScreenContract.Presenter mPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_details); // 初始化Presenter mPresenter = new DetailsScreenPresenter(new ApiService()); mPresenter.attachView(this); // 设置BackButton点击事件 findViewById(R.id.btn_back).setOnClickListener(v -> { mPresenter.onBackButtonClicked(); // 调用Presenter处理,而非直接返回 }); } @Override protected void onDestroy() { super.onDestroy(); mPresenter.detachView(); // 解绑Presenter,避免内存泄漏 } @Override public void navigateBack() { // 执行页面返回,根据你的导航方式选择: finish(); // 传统Activity返回 // 或用Navigation组件:NavHostFragment.findNavController(this).popBackStack(); } @Override public void showRequestError(String errorMsg) { Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show(); } }
方案二:原生实现(不用RxJava,用CountDownLatch+OkHttp)
如果项目未使用RxJava,可以用CountDownLatch跟踪请求完成状态,结合OkHttp的异步回调实现。
Presenter实现(DetailsScreenPresenter.java)
public class DetailsScreenPresenter implements DetailsScreenContract.Presenter { private DetailsScreenContract.View mView; private ApiService mApiService; private int mTotalRequests = 3; // 请求总数 private int mSuccessCount = 0; private boolean mIsRequesting = false; // 防止重复点击 public DetailsScreenPresenter(ApiService apiService) { mApiService = apiService; } @Override public void attachView(DetailsScreenContract.View view) { mView = view; } @Override public void detachView() { mView = null; } @Override public void onBackButtonClicked() { if (mIsRequesting) return; mIsRequesting = true; mSuccessCount = 0; // 发起第一个请求 mApiService.makeFirstRequest(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { if (response.isSuccessful()) { onSingleRequestSuccess(); } else { onSingleRequestFailed("第一个请求失败"); } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { onSingleRequestFailed(t.getMessage()); } }); // 发起第二个请求 mApiService.makeSecondRequest(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { if (response.isSuccessful()) { onSingleRequestSuccess(); } else { onSingleRequestFailed("第二个请求失败"); } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { onSingleRequestFailed(t.getMessage()); } }); // 发起第三个请求 mApiService.makeThirdRequest(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { if (response.isSuccessful()) { onSingleRequestSuccess(); } else { onSingleRequestFailed("第三个请求失败"); } } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { onSingleRequestFailed(t.getMessage()); } }); } private void onSingleRequestSuccess() { mSuccessCount++; if (mSuccessCount == mTotalRequests) { // 切换到主线程通知View返回 new Handler(Looper.getMainLooper()).post(() -> { if (mView != null) { mView.navigateBack(); } mIsRequesting = false; }); } } private void onSingleRequestFailed(String errorMsg) { // 切换到主线程提示错误 new Handler(Looper.getMainLooper()).post(() -> { if (mView != null) { mView.showRequestError(errorMsg); } mIsRequesting = false; }); } }
View层代码
和方案一的View层代码一致,点击BackButton调用mPresenter.onBackButtonClicked()即可。
关键注意事项
- 防止重复点击:用
mIsRequesting标记避免用户多次点击发起重复请求。 - 内存泄漏:Presenter在
detachView时要取消所有未完成的请求(RxJava用CompositeDisposable.dispose(),OkHttp可以保存所有Call对象并在detachView时调用call.cancel())。 - 线程切换:所有UI操作(包括通知View返回、显示Toast)必须在主线程执行,所以异步回调后要切换到主线程。
- 错误处理:如果某个请求失败,要及时通知用户,并且终止返回流程,避免部分请求未完成就返回。
内容的提问来源于stack exchange,提问作者Ghanem
相关产品推荐
相关产品推荐

