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

使用BottomNavigationView时,如何让Fragment仅加载一次Volley数据?

解决BottomNavigationView切换Fragment时重复加载Volley数据的问题

你遇到的这个问题其实很常见——用BottomNavigationView切换Fragment时,如果每次都是replace操作,Fragment的视图会被销毁重建,导致你的loadRecyclerViewData()方法被重复调用,反复发起不必要的网络请求。下面给你几个实用的解决方案,按推荐程度排序:

方案1:使用ViewModel缓存数据(最推荐,符合Jetpack架构)

ViewModel的生命周期和宿主Activity绑定,即使Fragment被销毁重建(比如切换回来、屏幕旋转),ViewModel里的数据依然保留。这样可以确保数据只请求一次,后续直接复用缓存。

步骤:

  1. 先创建一个ViewModel类,用来保存你的列表数据:
import androidx.lifecycle.ViewModel;
import java.util.ArrayList;
import java.util.List;

public class FirstFragmentViewModel extends ViewModel {
    private List<YourDataModel> dataList = new ArrayList<>();
    private boolean isDataLoaded = false;

    public List<YourDataModel> getDataList() {
        return dataList;
    }

    public void setDataList(List<YourDataModel> dataList) {
        this.dataList = dataList;
        isDataLoaded = true;
    }

    public boolean isDataLoaded() {
        return isDataLoaded;
    }
}

(这里的YourDataModel是你自己定义的实体类,用来解析JSON数据)

  1. 在你的Fragment里,获取ViewModel并判断是否需要加载数据:
import androidx.lifecycle.ViewModelProvider;

public class FirstFragment extends Fragment {
    private FirstFragmentViewModel viewModel;
    private RecyclerView recyclerView;
    private ProgressBar bar;
    private boolean isViewInitialized = false;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_first, container, false);
        recyclerView = view.findViewById(R.id.recyclerView);
        bar = view.findViewById(R.id.progressBar);
        // 初始化ViewModel
        viewModel = new ViewModelProvider(this).get(FirstFragmentViewModel.class);
        return view;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        isViewInitialized = true;
        checkAndLoadData();
    }

    @Override
    public void onResume() {
        super.onResume();
        // 确保视图初始化完成后再检查数据
        if (isViewInitialized) {
            checkAndLoadData();
        }
    }

    private void checkAndLoadData() {
        if (!viewModel.isDataLoaded()) {
            loadRecyclerViewData();
        } else {
            // 直接用缓存的数据填充RecyclerView
            bar.setVisibility(View.GONE);
            setupRecyclerView(viewModel.getDataList());
        }
    }

    private void loadRecyclerViewData() {
        StringRequest stringRequest = new StringRequest(Request.Method.GET, URL_DATA,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String s) {
                        bar.setVisibility(View.GONE);
                        try {
                            JSONArray array = new JSONArray(s);
                            List<YourDataModel> list = new ArrayList<>();
                            for (int i = 0; i < array.length(); i++) {
                                JSONObject o = array.getJSONObject(i);
                                // 解析数据到实体类
                                YourDataModel model = new YourDataModel();
                                model.setId(o.getInt("id"));
                                model.setTitle(o.getString("title"));
                                // ... 其他字段解析
                                list.add(model);
                            }
                            // 把数据存入ViewModel缓存
                            viewModel.setDataList(list);
                            // 填充RecyclerView
                            setupRecyclerView(list);
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        bar.setVisibility(View.GONE);
                        // 处理请求错误,比如显示提示
                    }
                });

        // 添加请求到Volley队列
        Volley.newRequestQueue(getContext()).add(stringRequest);
    }

    private void setupRecyclerView(List<YourDataModel> dataList) {
        YourAdapter adapter = new YourAdapter(dataList);
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        recyclerView.setAdapter(adapter);
    }
}

方案2:给Fragment添加加载状态标志位(简单直接,适合小型项目)

如果你不想引入ViewModel,可以给Fragment加一个布尔变量,标记数据是否已经加载完成。为了应对Fragment被系统销毁重建的情况,最好配合onSaveInstanceState保存状态。

代码示例:

public class FirstFragment extends Fragment {
    private boolean isDataLoaded = false;
    private RecyclerView recyclerView;
    private ProgressBar bar;
    private static final String KEY_LOADED_STATE = "key_loaded_state";
    // 缓存数据,避免Fragment重建后丢失
    private List<YourDataModel> cachedData;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_first, container, false);
        recyclerView = view.findViewById(R.id.recyclerView);
        bar = view.findViewById(R.id.progressBar);
        // 恢复保存的状态和数据
        if (savedInstanceState != null) {
            isDataLoaded = savedInstanceState.getBoolean(KEY_LOADED_STATE);
            cachedData = savedInstanceState.getParcelableArrayList("key_cached_data");
        }
        return view;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        if (!isDataLoaded) {
            loadRecyclerViewData();
        } else {
            bar.setVisibility(View.GONE);
            setupRecyclerView(cachedData);
        }
    }

    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        // 保存加载状态和缓存数据
        outState.putBoolean(KEY_LOADED_STATE, isDataLoaded);
        outState.putParcelableArrayList("key_cached_data", (ArrayList<YourDataModel>) cachedData);
    }

    private void loadRecyclerViewData() {
        StringRequest stringRequest = new StringRequest(Request.Method.GET, URL_DATA,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String s) {
                        bar.setVisibility(View.GONE);
                        try {
                            JSONArray array = new JSONArray(s);
                            cachedData = new ArrayList<>();
                            for (int i = 0; i < array.length(); i++) {
                                JSONObject o = array.getJSONObject(i);
                                YourDataModel model = new YourDataModel();
                                model.setId(o.getInt("id"));
                                model.setTitle(o.getString("title"));
                                cachedData.add(model);
                            }
                            setupRecyclerView(cachedData);
                            isDataLoaded = true;
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        bar.setVisibility(View.GONE);
                    }
                });

        Volley.newRequestQueue(getContext()).add(stringRequest);
    }

    private void setupRecyclerView(List<YourDataModel> dataList) {
        YourAdapter adapter = new YourAdapter(dataList);
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        recyclerView.setAdapter(adapter);
    }
}

方案3:修改BottomNavigationView切换逻辑,用hide/show替代replace

默认的replace操作会销毁旧Fragment,改用hide和show可以让Fragment实例一直保留,生命周期不会走到onDestroy,所以数据只会加载一次。

步骤:

  1. 在Activity里初始化所有Fragment并管理切换:
public class MainActivity extends AppCompatActivity {
    private FirstFragment firstFragment;
    private SecondFragment secondFragment;
    private ThirdFragment thirdFragment;
    private Fragment currentFragment;

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

        // 初始化所有Fragment实例
        firstFragment = new FirstFragment();
        secondFragment = new SecondFragment();
        thirdFragment = new ThirdFragment();

        // 默认显示第一个Fragment
        getSupportFragmentManager().beginTransaction()
                .add(R.id.fragment_container, thirdFragment, "third")
                .add(R.id.fragment_container, secondFragment, "second")
                .add(R.id.fragment_container, firstFragment, "first")
                .hide(thirdFragment)
                .hide(secondFragment)
                .show(firstFragment)
                .commit();
        currentFragment = firstFragment;

        // 设置BottomNavigationView监听
        BottomNavigationView bottomNav = findViewById(R.id.bottom_navigation);
        bottomNav.setOnNavigationItemSelectedListener(item -> {
            switch (item.getItemId()) {
                case R.id.nav_first:
                    switchFragment(firstFragment);
                    return true;
                case R.id.nav_second:
                    switchFragment(secondFragment);
                    return true;
                case R.id.nav_third:
                    switchFragment(thirdFragment);
                    return true;
                default:
                    return false;
            }
        });
    }

    private void switchFragment(Fragment targetFragment) {
        if (currentFragment != targetFragment) {
            getSupportFragmentManager().beginTransaction()
                    .hide(currentFragment)
                    .show(targetFragment)
                    .commit();
            currentFragment = targetFragment;
        }
    }
}
  1. 在FirstFragment里,把加载逻辑放在onViewCreated里(因为实例不会销毁,只会执行一次):
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    recyclerView = view.findViewById(R.id.recyclerView);
    bar = view.findViewById(R.id.progressBar);
    // 只会执行一次,因为Fragment实例一直存在
    loadRecyclerViewData();
}

这个方案的优点是简单,但缺点是所有Fragment会一直驻留在内存中,适合Fragment数量少、内存占用低的场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:18:58