Android底部导航切换时Fragment重复调用Retrofit API问题求助
解决方案:避免Fragment切换时重复调用API
方法1:使用ViewModel托管API请求(推荐)
把API请求逻辑绑定到ViewModel中,利用ViewModel与Activity生命周期绑定的特性,确保API仅在首次触发时请求一次,后续切换Fragment直接复用缓存数据:
实现代码
// 自定义ViewModel类 public class MainViewModel extends ViewModel { private MutableLiveData<List<DataModel>> dataLiveData; private ApiService apiService; private boolean isRequestInitiated = false; public MainViewModel() { apiService = RetrofitClient.getInstance().getApiService(); dataLiveData = new MutableLiveData<>(); } public LiveData<List<DataModel>> getData() { // 仅在未发起过请求且无缓存数据时调用API if (!isRequestInitiated && dataLiveData.getValue() == null) { fetchData(); } return dataLiveData; } private void fetchData() { isRequestInitiated = true; apiService.fetchData().enqueue(new Callback<List<DataModel>>() { @Override public void onResponse(Call<List<DataModel>> call, Response<List<DataModel>> response) { if (response.isSuccessful()) { dataLiveData.postValue(response.body()); } } @Override public void onFailure(Call<List<DataModel>> call, Throwable t) { // 请求失败时重置标记,允许用户重试 isRequestInitiated = false; } }); } }
在Fragment中调用:
public class HomeFragment extends Fragment { private MainViewModel viewModel; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 获取与宿主Activity绑定的ViewModel实例 viewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_home, container, false); // 观察数据变化更新UI viewModel.getData().observe(getViewLifecycleOwner(), data -> { // 这里写UI更新逻辑 }); return view; } }
方法2:在Fragment内添加请求标记位
如果不想引入ViewModel,可在Fragment内部添加布尔标记,控制仅首次加载时调用API:
public class HomeFragment extends Fragment { private boolean isDataLoaded = false; private ApiService apiService; @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); apiService = RetrofitClient.getInstance().getApiService(); // 仅未加载过数据时发起请求 if (!isDataLoaded) { fetchData(); isDataLoaded = true; } } private void fetchData() { apiService.fetchData().enqueue(new Callback<List<DataModel>>() { @Override public void onResponse(Call<List<DataModel>> call, Response<List<DataModel>> response) { if (response.isSuccessful()) { // 更新UI } } @Override public void onFailure(Call<List<DataModel>> call, Throwable t) { // 请求失败重置标记,允许重试 isDataLoaded = false; } }); } // 保存标记状态,避免屏幕旋转等场景下重复请求 @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean("isDataLoaded", isDataLoaded); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { isDataLoaded = savedInstanceState.getBoolean("isDataLoaded"); } } }
方法3:优化底部导航的Fragment复用逻辑
默认底部导航切换会销毁重建Fragment,可改为复用已创建的实例,从根源避免重复触发生命周期方法:
public class MainActivity extends AppCompatActivity { private Fragment currentFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); BottomNavigationView navView = findViewById(R.id.nav_view); navView.setOnItemSelectedListener(item -> { Fragment fragment = null; String tag = ""; switch (item.getItemId()) { case R.id.navigation_home: tag = "home_fragment"; // 先查找已存在的Fragment实例 fragment = getSupportFragmentManager().findFragmentByTag(tag); if (fragment == null) { fragment = new HomeFragment(); } break; // 其他导航项同理处理 } if (fragment != null && fragment != currentFragment) { getSupportFragmentManager().beginTransaction() .replace(R.id.nav_host_fragment, fragment, tag) .commit(); currentFragment = fragment; } return true; }); // 初始化默认显示的Fragment if (savedInstanceState == null) { navView.setSelectedItemId(R.id.navigation_home); } } }
内容的提问来源于stack exchange,提问作者Md Mushaddek Al Mubdi
相关产品推荐
相关产品推荐

