如何等待Retrofit的enqueue执行完成且不阻塞UI线程?
问题:异步请求完成后再切换Fragment且不阻塞UI线程
我通过以下代码在按钮的onClick()方法中发起异步注册请求:
AuthAPI authAPI = retrofitService.getRetrofit().create(AuthAPI.class); authAPI.registerUser(requestBody) .enqueue(new Callback<ResponseBody>() { @Override public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) { // 响应处理 } @Override public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) { // 失败处理 } });
现在需要在请求完成后替换当前Fragment,但直接在onClick()里切换会因为异步请求未完成导致崩溃。用execute()会阻塞UI线程,让ProgressBar动画卡住,不符合需求。想知道怎么在等待响应的同时不阻塞UI,保证ProgressBar正常运行。
解决方案1:在回调内执行Fragment切换(最直接方案)
核心逻辑就是把Fragment切换代码放到onResponse和onFailure回调里——这两个方法本身就在UI线程执行,既不会阻塞UI,又能确保请求完成后再执行切换:
button.setOnClickListener(v -> { // 点击后先显示ProgressBar progressBar.setVisibility(View.VISIBLE); AuthAPI authAPI = retrofitService.getRetrofit().create(AuthAPI.class); authAPI.registerUser(requestBody) .enqueue(new Callback<ResponseBody>() { @Override public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) { progressBar.setVisibility(View.GONE); // 先处理业务逻辑,比如校验返回结果 if (response.isSuccessful()) { // 请求完成后切换Fragment getParentFragmentManager().beginTransaction() .replace(R.id.container, new TargetFragment()) .addToBackStack(null) .commit(); } else { // 处理请求成功但业务失败的情况(比如注册重复提示) } } @Override public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) { progressBar.setVisibility(View.GONE); // 处理请求失败(比如弹窗提示网络错误) } }); });
解决方案2:使用Kotlin协程(推荐,代码更简洁)
如果项目用Kotlin,配合Retrofit的挂起函数可以实现更简洁的异步处理,且自动管理线程切换:
- 先把接口改成挂起函数:
interface AuthAPI { @POST("register") suspend fun registerUser(@Body requestBody: RequestBody): Response<ResponseBody> }
- 在Fragment的生命周期协程里执行请求:
button.setOnClickListener { lifecycleScope.launch { progressBar.visibility = View.VISIBLE try { val response = authAPI.registerUser(requestBody) if (response.isSuccessful) { // 请求完成后切换Fragment parentFragmentManager.beginTransaction() .replace(R.id.container, TargetFragment()) .addToBackStack(null) .commit() } } catch (e: Exception) { // 处理请求失败逻辑 } finally { progressBar.visibility = View.GONE } } }
lifecycleScope会绑定Fragment生命周期,避免内存泄漏;挂起函数registerUser在后台执行请求,不会阻塞UI,ProgressBar动画正常运行。
解决方案3:LiveData + ViewModel(MVVM架构方案)
如果遵循MVVM架构,可以把请求逻辑放到ViewModel中,用LiveData通知Fragment处理结果:
- 编写ViewModel类:
public class AuthViewModel extends ViewModel { private final MutableLiveData<Boolean> registerResult = new MutableLiveData<>(); private final MutableLiveData<Throwable> error = new MutableLiveData<>(); private final AuthAPI authAPI; public AuthViewModel() { authAPI = retrofitService.getRetrofit().create(AuthAPI.class); } public void registerUser(RequestBody requestBody) { authAPI.registerUser(requestBody) .enqueue(new Callback<ResponseBody>() { @Override public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) { registerResult.postValue(response.isSuccessful()); } @Override public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) { error.postValue(t); } }); } public LiveData<Boolean> getRegisterResult() { return registerResult; } public LiveData<Throwable> getError() { return error; } }
- 在Fragment中观察LiveData:
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); AuthViewModel authViewModel = new ViewModelProvider(this).get(AuthViewModel.class); button.setOnClickListener(v -> { progressBar.setVisibility(View.VISIBLE); authViewModel.registerUser(requestBody); }); // 观察注册结果 authViewModel.getRegisterResult().observe(getViewLifecycleOwner(), isSuccess -> { progressBar.setVisibility(View.GONE); if (isSuccess) { getParentFragmentManager().beginTransaction() .replace(R.id.container, new TargetFragment()) .addToBackStack(null) .commit(); } }); // 观察错误信息 authViewModel.getError().observe(getViewLifecycleOwner(), throwable -> { progressBar.setVisibility(View.GONE); // 处理错误提示 }); }
这种方式将业务逻辑与UI分离,更利于代码维护,同时也不会阻塞UI线程。
内容的提问来源于stack exchange,提问作者Atemyn
相关产品推荐
相关产品推荐

