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

Android WindowLeaked错误求助:已调用dismiss仍崩溃

解决Android WindowLeaked错误与AsyncTask中的Dialog问题

看起来你遇到了典型的WindowLeaked错误,这个问题的核心不是你没调用dismiss(),而是你的AsyncTask和Activity的生命周期没有同步——当后台任务还在运行时,你的DetailsActivity可能已经被销毁了(比如用户按了返回键),这时候再去操作依附在Activity上的Dialog,就会触发这个泄漏错误并导致应用崩溃。

一、先搞懂WindowLeaked错误的原因

android.view.WindowLeaked: Activity com.mqa.android.peminjamankelas.DetailsActivity has leaked window...

这个日志明确告诉你:你的DetailsActivity已经被销毁,但之前显示的Dialog还没有被正确移除,它还持有Activity的引用,导致内存泄漏,系统因此抛出错误并强制关闭应用。

你代码里虽然在onPostExecute()调用了pDialog.dismiss(),但如果在后台任务运行期间,Activity已经被finish(比如用户退出页面),那么onPostExecute()执行时,Activity已经不存在了,这时候调用dismiss()就会触发崩溃。

二、针对你的代码的修复步骤

1. 先检查Activity状态再操作Dialog

修改onPostExecute()方法,先判断Activity是否还存活,再执行Dialog和Intent操作:

@Override
protected void onPostExecute(String message) {
    // 先判断Activity是否还处于活跃状态
    if (DetailsActivity.this.isFinishing() || DetailsActivity.this.isDestroyed()) {
        return;
    }
    // 确保Dialog存在且显示时才关闭
    if (pDialog != null && pDialog.isShowing()) {
        pDialog.dismiss();
    }
    // 根据接口返回的message显示提示,而不是固定显示成功
    if (message != null) {
        Toast.makeText(DetailsActivity.this, message, Toast.LENGTH_SHORT).show();
    } else {
        Toast.makeText(DetailsActivity.this, "请求失败,请重试", Toast.LENGTH_SHORT).show();
    }
    // 只有请求成功时才跳转页面
    if (message != null && message.contains("Berhasil")) {
        Intent i = new Intent(DetailsActivity.this, MainActivity.class);
        startActivity(i);
        // 跳转后finish当前Activity,避免用户返回后看到无效页面
        DetailsActivity.this.finish();
    }
}

2. 把AsyncTask改成静态内部类,避免内存泄漏

你的BuatPeminjaman是普通内部类,会持有外部DetailsActivity的强引用——即使Activity销毁了,只要AsyncTask还在运行,Activity就无法被回收,这会加重内存泄漏问题。改成静态内部类并使用弱引用:

// 静态内部类+弱引用,避免持有Activity强引用
private static class BuatPeminjaman extends AsyncTask<String, String, String> {
    private WeakReference<DetailsActivity> activityRef;
    private ProgressDialog pDialog;
    private String gedung, lantai, nim, nama, peserta, keperluan, ruang;
    private JsonParser jsonParser;
    private static final String LOGIN_URL = "你的请求URL";
    private static final String TAG_SUCCESS = "success";
    private static final String TAG_MESSAGE = "message";

    // 构造方法传入Activity和请求参数
    public BuatPeminjaman(DetailsActivity activity, String gedung, String lantai, String nim, String nama, String peserta, String keperluan, String ruang) {
        this.activityRef = new WeakReference<>(activity);
        this.gedung = gedung;
        this.lantai = lantai;
        this.nim = nim;
        this.nama = nama;
        this.peserta = peserta;
        this.keperluan = keperluan;
        this.ruang = ruang;
        this.jsonParser = new JsonParser();
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        DetailsActivity activity = activityRef.get();
        // 如果Activity已销毁,直接取消任务
        if (activity == null || activity.isFinishing()) {
            cancel(true);
            return;
        }
        pDialog = new ProgressDialog(activity);
        pDialog.setMessage("Booking...");
        pDialog.setIndeterminate(false);
        pDialog.setCancelable(true);
        pDialog.show();
    }

    @Override
    protected String doInBackground(String... arg) {
        // 如果任务已取消,直接返回
        if (isCancelled()) {
            return null;
        }
        int success;
        try {
            List<NameValuePair> params = new ArrayList<NameValuePair>();
            params.add(new BasicNameValuePair("gedung", gedung));
            params.add(new BasicNameValuePair("lantai", lantai));
            params.add(new BasicNameValuePair("nim", nim));
            params.add(new BasicNameValuePair("nama", nama));
            params.add(new BasicNameValuePair("jumlah peserta", peserta));
            params.add(new BasicNameValuePair("keperluan", keperluan));
            params.add(new BasicNameValuePair("ruang", ruang));

            Log.d("request!", "starting");
            JSONObject json = jsonParser.makeHttpRequest(LOGIN_URL, "GET", params);
            Log.d("proses membuat", json.toString());

            success = json.getInt(TAG_SUCCESS);
            if (success == 1) {
                Log.d("Berhasil membuat !", json.toString());
                return json.getString(TAG_MESSAGE);
            } else {
                return json.getString(TAG_MESSAGE);
            }
        } catch (JSONException e) {
            e.printStackTrace();
            return "解析数据出错,请检查接口返回格式";
        } catch (Exception e) {
            e.printStackTrace();
            return "网络请求失败,请检查网络连接";
        }
    }

    @Override
    protected void onPostExecute(String message) {
        DetailsActivity activity = activityRef.get();
        if (activity == null || activity.isFinishing() || activity.isDestroyed()) {
            return;
        }
        if (pDialog != null && pDialog.isShowing()) {
            pDialog.dismiss();
        }
        if (message != null) {
            Toast.makeText(activity, message, Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(activity, "请求失败,请重试", Toast.LENGTH_SHORT).show();
        }
        // 只有成功时跳转页面
        if (message != null && message.contains("Berhasil")) {
            Intent i = new Intent(activity, MainActivity.class);
            activity.startActivity(i);
            activity.finish();
        }
    }
}

调用时改成:

// 在DetailsActivity的点击事件中调用
new BuatPeminjaman(this, gedung, lantai, nim, nama, peserta, keperluan, ruang).execute();

3. 排查JSON请求的潜在问题

从你的代码看,JSON请求如果失败(比如网络错误、服务器返回非JSON格式数据),会进入JSONException,但之前只打印了异常没有返回明确错误信息,这会导致onPostExecute()收到null,进而显示模糊的错误提示。上面的修复已经在catch块里添加了明确的错误返回,方便你调试。

另外,注意你用的是GET方法传递参数,如果参数包含中文或特殊字符,可能会导致解析错误,建议换成POST方法,或者对参数进行URL编码。

三、更现代的替代方案(推荐)

AsyncTask已经被Android官方废弃,更推荐使用ViewModel + Coroutines或者Retrofit来处理异步网络请求,这些方案能更好地管理生命周期,从根源避免内存泄漏:

比如用Coroutines的示例(Kotlin):

// 在DetailsActivity中
private fun submitBooking() {
    val progressDialog = ProgressDialog(this).apply {
        setMessage("Booking...")
        setIndeterminate(false)
        setCancelable(true)
        show()
    }
    lifecycleScope.launch {
        try {
            // 这里用Retrofit调用接口
            val response = apiService.submitBooking(gedung, lantai, nim, nama, peserta, keperluan, ruang)
            progressDialog.dismiss()
            if (response.success == 1) {
                Toast.makeText(this@DetailsActivity, response.message, Toast.LENGTH_SHORT).show()
                startActivity(Intent(this@DetailsActivity, MainActivity::class.java))
                finish()
            } else {
                Toast.makeText(this@DetailsActivity, response.message, Toast.LENGTH_SHORT).show()
            }
        } catch (e: Exception) {
            progressDialog.dismiss()
            Toast.makeText(this@DetailsActivity, "请求失败:${e.message}", Toast.LENGTH_SHORT).show()
            e.printStackTrace()
        }
    }
}

这种方式会自动和Activity的生命周期绑定,当Activity销毁时,协程会自动取消,不会出现WindowLeaked的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:11:43