横竖屏切换时Fragment避免重复请求API的实例状态保存咨询
嘿,这个问题我太有共鸣了——屏幕一旋转,Fragment就重新拉API,不仅浪费流量还影响体验。结合你的NowPlayingFragment代码,给你三个靠谱的解决方案,一步步来:
方案1:用
setRetainInstance(true)(传统快速方案) 这个方法能让你的Fragment在Activity重建时不被销毁,直接保留实例,所以成员变量里的arrayList数据自然就不会丢。
怎么实现?
在Fragment的onCreate()方法里加一行核心代码:
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); // 关键:让Fragment实例在Activity重建时保留 }
注意事项
- 虽然Fragment实例保留了,但View会在旋转时重建,所以要在
onDestroyView()里把View相关变量置空,避免内存泄漏:
@Override public void onDestroyView() { super.onDestroyView(); searchMovie = null; btnSearch = null; mRecyclerView = null; mAdapter = null; }
- 别让Fragment持有Activity的强引用(比如不要把
getActivity()赋值给成员变量),不然容易引发内存泄漏。
方案2:用
onSaveInstanceState保存数据(系统标准方式) 如果不想保留Fragment实例,可以用系统自带的状态保存机制,把已加载的arrayList存进Bundle,旋转后再取出来复用。
前提条件
你的Movie类必须实现Serializable或者Parcelable接口(推荐Parcelable,效率更高),这样才能被Bundle序列化存储。比如用Android Studio自动生成Parcelable实现:
public class Movie implements Parcelable { // 你的Movie字段(比如id、title、posterUrl等) // 自动生成Parcelable的构造器、writeToParcel、describeContents等方法 }
保存数据
在Fragment里重写onSaveInstanceState,把列表存进Bundle:
@Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putParcelableArrayList("saved_movie_list", arrayList); // 存储数据 }
恢复数据
在onCreateView或者onViewCreated里判断是否有保存的数据,有就直接复用,不用请求API:
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.your_fragment_layout, container, false); // 初始化RecyclerView、按钮等View... if (savedInstanceState != null) { // 从Bundle取出之前保存的列表 arrayList = savedInstanceState.getParcelableArrayList("saved_movie_list"); if (arrayList != null) { // 直接给RecyclerView设置适配器,跳过API请求 mAdapter = new YourMovieAdapter(arrayList); mRecyclerView.setAdapter(mAdapter); return view; } } // 没有保存的数据时,才去请求API loadDataFromApi(); return view; }
方案3:使用ViewModel(Jetpack推荐,最稳定)
这是现在Android开发的最佳实践,ViewModel的生命周期独立于Activity/Fragment的重建——屏幕旋转时ViewModel不会被销毁,数据自然保留,还能避免内存泄漏。
步骤1:添加ViewModel依赖
在模块级build.gradle里添加依赖(如果还没加):
dependencies { def lifecycle_version = "2.6.2" implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version" }
步骤2:创建ViewModel类
新建一个专门存数据的MovieViewModel:
import androidx.lifecycle.ViewModel; import java.util.ArrayList; public class MovieViewModel extends ViewModel { private ArrayList<Movie> movieList; public ArrayList<Movie> getMovieList() { return movieList; } public void setMovieList(ArrayList<Movie> movieList) { this.movieList = movieList; } }
步骤3:在Fragment中使用ViewModel
修改你的NowPlayingFragment,从ViewModel中读取和存储数据:
public class NowPlayingFragment extends Fragment { private EditText searchMovie; private Button btnSearch; private String lang; private RecyclerView mRecyclerView; private RecyclerView.Adapter mAdapter; private RecyclerView.LayoutManager mLayoutManager; private MovieViewModel viewModel; // 新增ViewModel变量 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 获取ViewModel,关联Fragment生命周期 viewModel = new ViewModelProvider(this).get(MovieViewModel.class); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.your_fragment_layout, container, false); // 初始化View和RecyclerView... // 先从ViewModel取数据 ArrayList<Movie> savedList = viewModel.getMovieList(); if (savedList != null && !savedList.isEmpty()) { // 有保存的数据,直接显示 arrayList = savedList; mAdapter = new YourMovieAdapter(arrayList); mRecyclerView.setAdapter(mAdapter); } else { // 无数据时请求API loadDataFromApi(); } // 搜索按钮点击后,更新数据要同步到ViewModel btnSearch.setOnClickListener(v -> { // 执行搜索逻辑... loadSearchResults(); }); return view; } // 假设这是你的API加载方法 private void loadDataFromApi() { yourApiClient.getNowPlayingMovies(lang, new Callback() { @Override public void onSuccess(ArrayList<Movie> movies) { arrayList = movies; mAdapter = new YourMovieAdapter(arrayList); mRecyclerView.setAdapter(mAdapter); // 把数据存到ViewModel,确保旋转时不丢失 viewModel.setMovieList(arrayList); } @Override public void onFailure(Throwable t) { // 处理错误逻辑... } }); } }
为什么推荐这个方案?
- 完全规避内存泄漏风险,ViewModel会在Fragment真正销毁时才被清理
- 数据与UI逻辑分离,代码更清晰易维护
- 不仅支持屏幕旋转,还能应对Activity因其他系统原因重建的情况
总结
- 快速实现选方案1,但要注意内存泄漏坑
- 追求系统标准实现选方案2,适合小体量序列化数据
- 现代Android开发首选方案3,稳定、安全且扩展性强
内容的提问来源于stack exchange,提问作者Pieter Haro
相关产品推荐
相关产品推荐

