Retrofit call.enqueue在Fragment与BottomNavigationView的onNavigationItemSelected中无响应
你遇到的这个问题本质是异步操作的时序错误,加上可能的对象被GC回收的问题,导致Retrofit的回调永远不会触发,具体原因和解决方法如下:
核心原因
异步请求的时序问题
你的userProfileDetails()是一个同步方法,但内部使用了Retrofit的call.enqueue()——这是异步执行的网络请求。当你调用userProfile = userProfileDetails()时,方法会立刻返回初始值(大概率是null),然后继续执行创建Fragment、传递参数的逻辑,而此时网络请求还在后台跑,回调完全没机会执行。Call对象被提前回收
如果Call<UserProfile>对象只是作为userProfileDetails()方法的局部变量,当方法执行完毕后,这个对象可能被GC回收,导致网络请求的回调通道被切断,自然不会触发onResponse或onFailure。额外细节问题
你的onNavigationItemSelected返回了false,这会导致BottomNavigationView的菜单项不会被标记为选中状态,虽然和回调问题无关,但也是一个需要修正的细节。
解决方案
方案1:把网络请求逻辑移到UserProfileFragment内部
最合理的做法是让Fragment自己负责加载数据,这样可以避免时序问题,也符合组件的职责分离:
修改onNavigationItemSelected方法:
@Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { Fragment fragment = null; switch (item.getItemId()){ case R.id.menu_landingorder: fragment = new OrderFragment(); break; case R.id.menu_reccuringcart: fragment = new RecurringCartFragment(); break; case R.id.menu_landingprofile: fragment = new UserProfileFragment(); // 不需要提前传递userProfile,让Fragment自己请求数据 break; } if(fragment!=null){ displayFragment(fragment); } // 返回true,标记当前菜单项为选中状态 return true; }
然后在UserProfileFragment的onViewCreated方法里发起网络请求:
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // 从SessionManager获取token(根据你的实际实现调整) String token = "Bearer "+ yourSessionManager.getSessionToken(); Call<UserProfile> call = RetrofitClient.getInstance().getApi().getUserDetails(token); call.enqueue(new Callback<UserProfile>(){ @Override public void onResponse(Call<UserProfile> call, Response<UserProfile> response) { if(response.isSuccessful()){ UserProfile userProfile = response.body(); Log.i("Response SuccessFul :: ", userProfile.getPhoneNo()); // 拿到数据后更新Fragment的UI updateProfileUI(userProfile); }else { Log.e("Response Failure :: ", "请求失败,状态码: "+response.code()); try { if(response.errorBody() != null){ Log.e("错误详情", response.errorBody().string()); } } catch (IOException e) { Log.e("错误解析失败", e.getMessage()); } } } @Override public void onFailure(Call<UserProfile> call, Throwable t) { Log.e("网络请求失败 ", t.getMessage() ); } }); } private void updateProfileUI(UserProfile userProfile) { // 这里写更新UI的逻辑,比如给TextView设置手机号、昵称等 }
方案2:在Activity中发起请求,拿到数据后再创建Fragment
如果你一定要在Activity中处理请求,可以用回调的方式,等请求完成后再创建并显示Fragment:
首先定义一个回调接口:
interface OnProfileLoadedListener { void onProfileLoaded(UserProfile profile); void onProfileLoadFailed(Throwable error); }
修改userProfileDetails方法,接收回调:
// 把Call对象作为Activity的成员变量,避免被GC回收 private Call<UserProfile> profileCall; private void userProfileDetails(OnProfileLoadedListener listener) { String token = "Bearer "+ user.get(SessionManager.KEY_SESSION_TOKEN); Log.i("user :: " , token); profileCall = RetrofitClient.getInstance().getApi().getUserDetails(token); profileCall.enqueue(new Callback<UserProfile>(){ @Override public void onResponse(Call<UserProfile> call, Response<UserProfile> response) { if(response.isSuccessful()){ UserProfile userProfile = response.body(); Log.i("Response SuccessFul :: ", userProfile.getPhoneNo()); listener.onProfileLoaded(userProfile); }else { Log.e("Response Failure :: ", "请求失败,状态码: "+response.code()); listener.onProfileLoadFailed(new Exception("请求失败,状态码: "+response.code())); } } @Override public void onFailure(Call<UserProfile> call, Throwable t) { Log.e("网络请求失败 ", t.getMessage() ); listener.onProfileLoadFailed(t); } }); }
然后修改onNavigationItemSelected:
@Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { switch (item.getItemId()){ case R.id.menu_landingorder: displayFragment(new OrderFragment()); break; case R.id.menu_reccuringcart: displayFragment(new RecurringCartFragment()); break; case R.id.menu_landingprofile: // 发起请求,等回调拿到数据后再创建Fragment userProfileDetails(new OnProfileLoadedListener() { @Override public void onProfileLoaded(UserProfile profile) { UserProfileFragment fragment = new UserProfileFragment(); Bundle b = new Bundle(); b.putSerializable(StringConstants.USERPROFILE, profile); fragment.setArguments(b); displayFragment(fragment); } @Override public void onProfileLoadFailed(Throwable error) { // 处理加载失败的情况,比如显示错误提示 Toast.makeText(LandingActivity.this, "加载个人信息失败: "+error.getMessage(), Toast.LENGTH_SHORT).show(); // 可以显示一个空状态的Fragment displayFragment(new UserProfileFragment()); } }); break; } return true; }
同时,记得在Activity的onDestroy方法中取消请求,避免内存泄漏:
@Override protected void onDestroy() { super.onDestroy(); if(profileCall != null && !profileCall.isCanceled()){ profileCall.cancel(); } }
关键注意点
- 永远不要在同步方法里等待异步请求的结果(比如用
call.execute()同步请求,这会阻塞主线程,导致ANR)。 - 确保Retrofit的
Call对象被持有(比如作为类成员变量),避免被GC回收而导致回调丢失。 - 组件间的数据传递尽量遵循单一职责原则,让Fragment自己管理数据加载更清晰。
内容的提问来源于stack exchange,提问作者Visual Program

