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

安卓Java开发:如何避免屏幕旋转时ExecutorService后台任务重复加载数据

Android屏幕旋转导致地震数据重复加载的Java解决方案

根本原因

Android发生配置变更(如屏幕旋转、系统语言切换、分辨率调整)时,默认会销毁并重建当前Activity,你的网络请求逻辑直接写在onCreate生命周期方法中,Activity重建时onCreate会重新执行,就会重复发起后台请求加载数据。同时未做管控的后台线程还会持有已销毁Activity的引用,存在内存泄漏风险。

可行解决方案(Java环境)

下面给出3种常用实现方案,可根据你的项目需求选择:

方案1:配置configChanges手动处理旋转事件(适合横竖屏布局一致的场景,实现最简单)

直接在AndroidManifest.xml中为你的MainActivity添加配置变更声明,旋转时不会触发Activity重建:

<activity
    android:name=".MainActivity"
    android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
    android:exported="true">
    <!-- 其他原有配置 -->
</activity>

如果不需要在旋转时做特殊逻辑,不需要额外修改Activity代码,即可解决重复请求问题。如果横竖屏布局不同,需要重写onConfigurationChanged方法手动替换布局和更新视图。

方案2:通过onSaveInstanceState缓存已加载数据(适合数据量小的场景)

步骤如下:

  1. 让你的EarthquakeModel实体类实现Parcelable接口,支持序列化存储。
  2. 重写Activity的onSaveInstanceState方法,把已经加载好的地震列表缓存进去:
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putParcelableArrayList("earthquake_data", earthquake);
    outState.putBoolean("is_connected", isConnected);
}
  1. 修改onCreate逻辑,先判断是否有缓存数据,有缓存直接用,没有再发起请求:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    recyclerView = findViewById(R.id.recycler_view);
    nointernetLinearLayout = findViewById(R.id.no_internet);
    mEmptyView = findViewById(R.id.empty_view);
    earthquake = new ArrayList<>();

    // 先取缓存数据
    if (savedInstanceState != null) {
        earthquake = savedInstanceState.getParcelableArrayList("earthquake_data");
        isConnected = savedInstanceState.getBoolean("is_connected");
        // 直接渲染UI,不需要发起请求
        updateUI();
        return;
    }

    // 没有缓存才发起网络请求
    ExecutorService service = Executors.newSingleThreadExecutor();
    service.execute(new Runnable() {
        // 原有请求逻辑不变,请求成功后调用updateUI()渲染视图
    });
}

// 把UI渲染逻辑抽成公共方法,避免重复代码
private void updateUI() {
    View loadingIndicator = findViewById(R.id.loading_indicator);
    loadingIndicator.setVisibility(View.GONE);
    if (isConnected&&!earthquake.isEmpty()){
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext());
        linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(linearLayoutManager);
        EarthquakeAdapter earthquakeAdapter = new EarthquakeAdapter(earthquake,MainActivity.this);
        recyclerView.setAdapter(earthquakeAdapter);
    }if(isConnected&&earthquake.isEmpty()){
        recyclerView.setVisibility(View.GONE);
        mEmptyView.setVisibility(View.VISIBLE);
    }
    if (!isConnected){
        recyclerView.setVisibility(View.GONE);
        nointernetLinearLayout.setVisibility(View.VISIBLE);
    }
}

方案3:使用ViewModel托管数据和请求逻辑(官方推荐,适配性最强)

ViewModel是Android Jetpack提供的生命周期感知组件,配置变更时ViewModel不会销毁,托管的数据和正在执行的请求都不会中断,不会重复执行。

  1. 先引入ViewModel依赖(如果没引入的话,build.gradle(module)中添加):
dependencies {
    implementation "androidx.lifecycle:lifecycle-viewmodel:2.6.2"
    implementation "androidx.lifecycle:lifecycle-livedata:2.6.2"
}
  1. 新建EarthquakeViewModel类,继承AndroidViewModel:
public class EarthquakeViewModel extends AndroidViewModel {
    private MutableLiveData<ArrayList<EarthquakeModel>> earthquakeLiveData;
    private MutableLiveData<Boolean> isConnectedLiveData;
    private static final String url = "https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime=2021-09-10&endtime=2021-09-11";

    public EarthquakeViewModel(@NonNull Application application) {
        super(application);
    }

    public LiveData<ArrayList<EarthquakeModel>> getEarthquakeData() {
        if (earthquakeLiveData == null) {
            earthquakeLiveData = new MutableLiveData<>();
            isConnectedLiveData = new MutableLiveData<>();
            loadEarthquakeData();
        }
        return earthquakeLiveData;
    }

    public LiveData<Boolean> getIsConnected() {
        return isConnectedLiveData;
    }

    private void loadEarthquakeData() {
        ExecutorService service = Executors.newSingleThreadExecutor();
        service.execute(() -> {
            QueryUtils queryUtils = new QueryUtils();
            String json = queryUtils.call(url);
            ArrayList<EarthquakeModel> tempList = new ArrayList<>();
            boolean connected = false;
            try {
                ConnectivityManager cm = (ConnectivityManager) getApplication().getSystemService(Context.CONNECTIVITY_SERVICE);
                NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
                connected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();

                if (connected){
                    Log.i("Background task","only run once");
                    JSONObject jsonObject = new JSONObject(json);
                    JSONArray jsonArray = jsonObject.getJSONArray("features");
                    for (int i=0; i<jsonArray.length(); i++){
                        JSONObject c = jsonArray.getJSONObject(i);
                        JSONObject properties = c.getJSONObject("properties");
                        double magnitude = properties.getDouble("mag");
                        String location = properties.getString("place");
                        long time = properties.getLong("time");
                        String url = properties.getString("url");
                        EarthquakeModel earthquakeModel = new EarthquakeModel(location, magnitude,time,url);
                        tempList.add(earthquakeModel);
                    }
                }

            }catch (JSONException e){
                Log.e("Error","is"+e.getMessage());
            }
            // 把结果 post 到LiveData
            earthquakeLiveData.postValue(tempList);
            isConnectedLiveData.postValue(connected);
        });
    }
}
  1. 修改MainActivity的onCreate逻辑,只做观察者绑定:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    recyclerView = findViewById(R.id.recycler_view);
    nointernetLinearLayout = findViewById(R.id.no_internet);
    mEmptyView = findViewById(R.id.empty_view);
    View loadingIndicator = findViewById(R.id.loading_indicator);

    // 获取ViewModel实例
    EarthquakeViewModel viewModel = new ViewModelProvider(this).get(EarthquakeViewModel.class);
    // 观察数据变化
    viewModel.getEarthquakeData().observe(this, earthquakeList -> {
        loadingIndicator.setVisibility(View.GONE);
        boolean isConnected = Boolean.TRUE.equals(viewModel.getIsConnected().getValue());
        if (isConnected&&!earthquakeList.isEmpty()){
            LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getApplicationContext());
            linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
            recyclerView.setLayoutManager(linearLayoutManager);
            EarthquakeAdapter earthquakeAdapter = new EarthquakeAdapter(earthquakeList,MainActivity.this);
            recyclerView.setAdapter(earthquakeAdapter);
        }if(isConnected&&earthquakeList.isEmpty()){
            recyclerView.setVisibility(View.GONE);
            mEmptyView.setVisibility(View.VISIBLE);
        }
        if (!isConnected){
            recyclerView.setVisibility(View.GONE);
            nointernetLinearLayout.setVisibility(View.VISIBLE);
        }
    });
}

这个方案不仅解决了旋转重复请求的问题,还避免了内存泄漏,是长期维护项目的最优选择。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 02:39:02