如何在OnResponse外获取Retrofit响应并返回给官方推荐架构的Repository层
根本原因
你遇到的问题本质是异步调用和同步返回的时序冲突:Retrofit的enqueue方法是异步执行的,发起请求后会立刻执行后续代码,而onResponse回调要等到网络请求结束才会触发,你在回调执行前就返回了还没赋值的user,自然拿到的是空值。你想让异步方法直接同步返回对象的思路是不可行的,有三种成熟的解决方案。
方案1:全链路回调改造(Java适用)
从DataSource到Repository再到上层都采用回调传递结果,不要尝试让login方法同步返回Result。
public class LoginDataSource { public static Retrofit retrofit = null; // 去掉返回值,增加回调参数 public void login(String username, String password, final GetUserResponse callback) { try { retrofit = new Retrofit.Builder() .baseUrl("https://myserver") .addConverterFactory(GsonConverterFactory.create()) .build(); PostEndPoint endPoint = retrofit.create(PostEndPoint.class); Call<LoggedInUser> call = endPoint.getUser("login", username, password); call.enqueue(new Callback<LoggedInUser>() { @Override public void onResponse(Call<LoggedInUser> call, Response<LoggedInUser> response) { if (response.isSuccessful()){ // 直接把结果通过回调向上传 callback.onSuccess(new Result.Success<>(response.body())); } else { callback.onError(new Result.Error(new Exception("请求失败,错误码:" + response.code()))); } } @Override public void onFailure(Call<LoggedInUser> call, Throwable t) { callback.onError(new Result.Error(t)); } }); } catch (Exception e) { callback.onError(new Result.Error(e)); } } // 回调接口改成支持成功/失败两种状态 public interface GetUserResponse { void onSuccess(Result<LoggedInUser> result); void onError(Result.Error error); } }
Repository层对应改造即可,上层调用时直接在回调里处理结果:
public class LoginRepository { private LoginDataSource dataSource = new LoginDataSource(); public void login(String username, String password, LoginDataSource.GetUserResponse callback) { dataSource.login(username, password, callback); } }
方案2:RxJava适配(Java/Kotlin适用)
如果不想写多层嵌套回调,可以给Retrofit增加RxJava适配器,把请求转为Observable/Flowable,通过链式调用传递结果:
// 新增RxJava适配器依赖 // implementation 'com.squareup.retrofit2:adapter-rxjava3:版本号' public class LoginDataSource { public static Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://myserver") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) .build(); PostEndPoint endPoint = retrofit.create(PostEndPoint.class); public Observable<Result<LoggedInUser>> login(String username, String password) { return endPoint.getUser("login", username, password) .map(user -> (Result<LoggedInUser>) new Result.Success<>(user)) .onErrorReturn(e -> new Result.Error(e)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } } // PostEndPoint接口对应修改返回值 interface PostEndPoint { @GET("你的接口路径") Observable<LoggedInUser> getUser(@Query("type") String type, @Query("username") String username, @Query("password") String password); }
方案3:协程改造(Kotlin官方首选方案)
Kotlin环境下可以直接用Retrofit的suspend函数支持,把异步请求转成挂起函数,用同步写法拿到结果,完全避免回调嵌套:
// PostEndPoint接口增加suspend关键字 interface PostEndPoint { @GET("你的接口路径") suspend fun getUser(@Query("type") type: String, @Query("username") username: String, @Query("password") password: String): LoggedInUser } class LoginDataSource { private val retrofit = Retrofit.Builder() .baseUrl("https://myserver") .addConverterFactory(GsonConverterFactory.create()) .build() private val endPoint = retrofit.create(PostEndPoint::class.java) // 直接返回Result,不需要回调 suspend fun login(username: String, password: String): Result<LoggedInUser> { return try { val user = endPoint.getUser("login", username, password) Result.Success(user) } catch (e: Exception) { Result.Error(e) } } }
Repository和ViewModel层直接在协程作用域里调用login方法即可拿到结果。
注意:绝对不要在主线程直接调用
call.execute()执行同步请求,会阻塞主线程触发ANR异常。
内容的提问来源于stack exchange,提问作者maganthro
相关产品推荐
相关产品推荐

