You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Java/Android中等待Retrofit的call.enqueue异步请求?

嘿,从C#转Java/Android开发,肯定会怀念C#里await那种“等拿到结果再往下走”的顺畅感吧!我完全懂你现在面对Retrofit的call.enqueue()异步回调时,看着日志顺序乱掉、没法直接在LoginActivity里按顺序处理登录结果的苦恼😅

下面给你几种可行的方案,既能模拟类似await的等待效果,又符合Android的开发规范:

方法一:使用Retrofit的同步执行方法call.execute()

Retrofit本身提供了同步执行请求的方法call.execute(),它会阻塞当前线程直到请求完成,拿到响应结果后再继续执行后续代码,和你熟悉的await逻辑很像。

但要划重点:绝对不能在UI线程调用这个方法! Android禁止在主线程进行网络请求,会直接抛出异常导致APP崩溃,所以必须把这段逻辑放到后台线程里。比如用ExecutorService来实现:

// 在LoginActivity的登录方法中
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
    try {
        // 构造登录请求Call
        Call<LoginResponse> loginCall = loginService.login(yourCredentials);
        // 同步执行,阻塞当前线程直到拿到响应
        Response<LoginResponse> response = loginCall.execute();
        
        // 请求完成后,回到主线程处理结果(更新UI等操作必须在主线程)
        runOnUiThread(() -> {
            if (response.isSuccessful()) {
                // 登录成功,执行后续逻辑(比如跳转主页)
                Log.d("LoginActivity", "登录成功,开始处理后续步骤");
            } else {
                // 登录失败,提示错误
                Log.d("LoginActivity", "登录失败:" + response.message());
            }
        });
    } catch (IOException e) {
        e.printStackTrace();
        runOnUiThread(() -> {
            Log.d("LoginActivity", "网络请求出错:" + e.getMessage());
        });
    }
});
executor.shutdown();

方法二:用CountDownLatch手动阻塞等待

如果你还是想用call.enqueue()的异步回调,又想让当前线程等待回调完成,可以用Java的CountDownLatch来实现“等待通知”的逻辑:

// 同样要在后台线程执行这段代码
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
    CountDownLatch latch = new CountDownLatch(1);
    final Response<LoginResponse>[] responseHolder = new Response[1]; // 用数组存结果,因为匿名内部类只能访问final变量
    
    Call<LoginResponse> loginCall = loginService.login(yourCredentials);
    loginCall.enqueue(new Callback<LoginResponse>() {
        @Override
        public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
            responseHolder[0] = response;
            latch.countDown(); // 告诉等待的线程:请求完成了!
        }

        @Override
        public void onFailure(Call<LoginResponse> call, Throwable t) {
            t.printStackTrace();
            latch.countDown(); // 失败也要通知,避免线程一直阻塞
        }
    });

    try {
        latch.await(); // 阻塞当前线程,直到countDown()被调用
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    // 现在可以安全地处理响应结果了
    Response<LoginResponse> response = responseHolder[0];
    runOnUiThread(() -> {
        if (response != null && response.isSuccessful()) {
            Log.d("LoginActivity", "登录成功");
        } else {
            Log.d("LoginActivity", "登录失败");
        }
    });
});
executor.shutdown();

这个方法的核心是用CountDownLatch作为“开关”,回调完成后触发开关,等待的线程再继续执行。同样要注意:绝对不能在UI线程调用latch.await(),会导致ANR(应用无响应)!

更推荐的Android异步方案:ViewModel + LiveData

其实Android开发更推荐用“响应式”的异步处理方式,而不是强行阻塞线程。比如用ViewModel处理网络请求,再通过LiveData把结果通知给Activity,这样既符合Android的生命周期管理,又能避免ANR:

第一步:定义LoginViewModel

public class LoginViewModel extends ViewModel {
    private final MutableLiveData<LoginResult> loginResult = new MutableLiveData<>();
    private final LoginService loginService;

    public LoginViewModel() {
        // 初始化Retrofit的LoginService
        loginService = RetrofitClient.getInstance().create(LoginService.class);
    }

    // 给Activity提供观察结果的LiveData
    public LiveData<LoginResult> getLoginResult() {
        return loginResult;
    }

    // 登录方法,内部处理异步请求
    public void performLogin(UserCredentials credentials) {
        Call<LoginResponse> loginCall = loginService.login(credentials);
        loginCall.enqueue(new Callback<LoginResponse>() {
            @Override
            public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
                if (response.isSuccessful()) {
                    // 登录成功,发送成功结果
                    loginResult.postValue(new LoginResult.Success(response.body()));
                } else {
                    // 登录失败,发送错误信息
                    loginResult.postValue(new LoginResult.Error("登录失败:" + response.message()));
                }
            }

            @Override
            public void onFailure(Call<LoginResponse> call, Throwable t) {
                loginResult.postValue(new LoginResult.Error("网络错误:" + t.getMessage()));
            }
        });
    }

    // 定义结果封装类,区分成功和失败状态
    public abstract static class LoginResult {
        public static class Success extends LoginResult {
            private final LoginResponse data;
            public Success(LoginResponse data) { this.data = data; }
            public LoginResponse getData() { return data; }
        }

        public static class Error extends LoginResult {
            private final String message;
            public Error(String message) { this.message = message; }
            public String getMessage() { return message; }
        }
    }
}

第二步:在LoginActivity中观察结果

public class LoginActivity extends AppCompatActivity {
    private LoginViewModel loginViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        // 初始化ViewModel
        loginViewModel = new ViewModelProvider(this).get(LoginViewModel.class);

        // 观察登录结果,自动在主线程回调
        loginViewModel.getLoginResult().observe(this, result -> {
            if (result instanceof LoginViewModel.LoginResult.Success) {
                // 登录成功,处理后续步骤(比如跳转主页)
                Log.d("LoginActivity", "登录成功,开始处理后续逻辑");
            } else if (result instanceof LoginViewModel.LoginResult.Error) {
                // 登录失败,提示用户
                Log.d("LoginActivity", ((LoginViewModel.LoginResult.Error) result).getMessage());
            }
        });

        // 登录按钮点击事件
        findViewById(R.id.btn_login).setOnClickListener(v -> {
            UserCredentials credentials = new UserCredentials(
                ((EditText) findViewById(R.id.et_username)).getText().toString(),
                ((EditText) findViewById(R.id.et_password)).getText().toString()
            );
            // 调用ViewModel的登录方法
            loginViewModel.performLogin(credentials);
        });
    }
}

这种方式虽然不是直接“等待”请求完成,但能让你的代码逻辑更清晰,还能自动处理Activity的生命周期(比如屏幕旋转时不会丢失请求状态),是Android官方推荐的最佳实践哦!


内容的提问来源于stack exchange,提问作者Jeremy P

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 08:44:34