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

Android 9.0 Activity窗口泄漏求助:添加usesCleartextTraffic后ProgressDialog异常

解决Android 9.0中Volley请求后ProgressDialog窗口泄漏问题

我之前碰到过几乎一模一样的场景!当时也是为了让Volley支持明文请求加了android:usesCleartextTraffic="true",结果在Android 9.0上直接炸出了窗口泄漏的报错,其他版本都好好的。后来排查发现,这个问题其实和usesCleartextTraffic本身无关——它只是让你的Volley请求能正常跑起来,把之前隐藏的生命周期不一致问题给暴露出来了。

问题根源

Android 9.0(API 28)开始强化了窗口管理的检测机制,同时对非前台Activity显示窗口的限制更严格。如果你的Volley请求回调在Activity已经销毁(比如用户按了返回键)之后才执行,这时候去显示或操作ProgressDialog,就会导致Dialog附着在一个已经被销毁的Activity实例上,触发Activity has leaked window的报错。之前的Android版本可能没有明确抛出这个异常,但泄漏其实已经存在了。

具体解决步骤

1. 主动管理ProgressDialog的生命周期

在你的Activity的onDestroy()方法中,强制关闭并销毁Dialog,避免它持有Activity的引用:

private ProgressDialog mProgressDialog;

@Override
protected void onDestroy() {
    super.onDestroy();
    // 清理Dialog
    if (mProgressDialog != null) {
        if (mProgressDialog.isShowing()) {
            mProgressDialog.dismiss();
        }
        mProgressDialog = null; // 置空彻底释放引用
    }
}

2. 绑定Volley请求与Activity生命周期

在Activity销毁时,取消所有正在执行的Volley请求,避免回调在Activity销毁后触发:

private RequestQueue mVolleyQueue;
private Request<?> mCurrentRequest; // 保存当前正在执行的请求

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mVolleyQueue = Volley.newRequestQueue(this);
}

// 发起请求时保存请求实例
private void sendVolleyRequest() {
    StringRequest request = new StringRequest(Request.Method.GET, "你的接口地址",
            response -> {
                // 请求成功,关闭Dialog
                if (mProgressDialog != null && mProgressDialog.isShowing()) {
                    mProgressDialog.dismiss();
                }
                // 处理响应数据
            },
            error -> {
                // 请求失败,关闭Dialog
                if (mProgressDialog != null && mProgressDialog.isShowing()) {
                    mProgressDialog.dismiss();
                }
                // 处理错误
            });
    mCurrentRequest = request;
    mVolleyQueue.add(request);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    // 取消当前请求
    if (mCurrentRequest != null) {
        mCurrentRequest.cancel();
    }
    // 或者取消所有和当前Activity相关的请求(如果用了tag)
    mVolleyQueue.cancelAll(this);
    // 别忘了清理Dialog
    if (mProgressDialog != null) {
        if (mProgressDialog.isShowing()) {
            mProgressDialog.dismiss();
        }
        mProgressDialog = null;
    }
}

3. 进阶优化:用ViewModel解耦请求与UI

如果你的项目用了Jetpack,推荐用ViewModel来管理Volley请求,通过LiveData通知Activity更新UI状态。这样请求的生命周期和Activity分离,从根本上避免生命周期不一致的问题:

// 自定义ViewModel
public class MyViewModel extends ViewModel {
    private MutableLiveData<Boolean> mIsLoading = new MutableLiveData<>();
    private RequestQueue mVolleyQueue;

    public MyViewModel() {
        mVolleyQueue = Volley.newRequestQueue(MyApplication.getInstance());
    }

    public LiveData<Boolean> getIsLoading() {
        return mIsLoading;
    }

    public void sendRequest() {
        mIsLoading.postValue(true);
        StringRequest request = new StringRequest(Request.Method.GET, "你的接口地址",
                response -> {
                    mIsLoading.postValue(false);
                    // 处理响应
                },
                error -> {
                    mIsLoading.postValue(false);
                    // 处理错误
                });
        mVolleyQueue.add(request);
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        // ViewModel销毁时取消所有请求
        mVolleyQueue.cancelAll(this);
    }
}

// 在Activity中观察状态
public class MyActivity extends AppCompatActivity {
    private ProgressDialog mProgressDialog;

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

        MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
        viewModel.getIsLoading().observe(this, isLoading -> {
            if (isLoading) {
                showProgressDialog();
            } else {
                hideProgressDialog();
            }
        });

        // 发起请求
        viewModel.sendRequest();
    }

    private void showProgressDialog() {
        if (mProgressDialog == null) {
            mProgressDialog = ProgressDialog.show(this, "加载中", "请稍后...");
        } else if (!mProgressDialog.isShowing()) {
            mProgressDialog.show();
        }
    }

    private void hideProgressDialog() {
        if (mProgressDialog != null && mProgressDialog.isShowing()) {
            mProgressDialog.dismiss();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mProgressDialog != null) {
            mProgressDialog.dismiss();
            mProgressDialog = null;
        }
    }
}

总结

这个问题的本质是Volley请求回调时机和Activity生命周期不同步,Android 9.0只是把之前隐藏的泄漏问题明确报错了。按照上面的方法处理后,就能彻底解决这个窗口泄漏的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 08:52:47