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

如何解决Android的android.view.WindowLeaked错误及图片上传崩溃问题

解决Android View.WindowLeaked错误及AsyncTask网络检查问题

首先,咱们来拆解你遇到的android.view.WindowLeaked错误——这个问题主要出在三个核心点:后台线程直接操作UI、Dialog依附的Context生命周期不匹配,再加上你的网络检查逻辑存在反转错误,咱们一步步来修复:

错误原因分析

  1. 后台线程操作UI:你的UploadingTask.doInBackground里直接调用了showAlert(),而doInBackground是在后台线程执行的,Android严格禁止在非UI线程更新UI(包括弹出Dialog),这不仅会引发异常,还会导致窗口泄漏。
  2. Context生命周期问题:你用getContext()创建AlertDialog和ProgressDialog,但如果Fragment已经和Activity分离(比如用户切换了页面),这个Context就会失效,Dialog还挂着就会触发WindowLeaked。
  3. 网络检查逻辑反转:看你的hasInternetAccess方法,当没有网络时居然返回true,有网络时才去验证连接,这完全搞反了逻辑,会导致网络状态判断错误。

分步解决方案

1. 修正网络检查逻辑

先把hasInternetAccess的判断逻辑改对,确保返回值符合预期:

public static boolean hasInternetAccess(Context context) {
    if (isNetworkAvailable(context)) {
        try {
            HttpURLConnection urlc = (HttpURLConnection) (new URL("http://clients3.google.com/generate_204")
                    .openConnection());
            urlc.setRequestProperty("User-Agent", "Android");
            urlc.setRequestProperty("Connection", "close");
            urlc.setConnectTimeout(1500);
            urlc.connect();
            return (urlc.getResponseCode() == 204 && urlc.getContentLength() == 0);
        } catch (IOException e) {
            Log.e(TAG, "Error checking internet connection", e);
        }
    } 
    // 没有网络或者网络不可用,返回false
    return false;
}

2. 禁止在后台线程操作UI

把doInBackground里的UI操作(弹AlertDialog)移到onPostExecute,后台线程只负责网络检查和上传逻辑,同时处理Firebase异步上传的等待问题:

class UploadingTask extends AsyncTask<String, Void, Boolean> {
    private boolean isNetworkOk = true;

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        if (!isFormValid()) {
            showAlert("Error", "Please fill all the details");
            // 如果表单无效,直接取消任务
            cancel(true);
            return;
        }
        showProgressDialog(true);
    }

    @Override
    protected Boolean doInBackground(String... params) {
        if (isCancelled()) return false;
        
        String catValue = params[0];
        // 后台只做网络检查和上传,不碰UI
        if (!hasInternetAccess(getContext())) {
            isNetworkOk = false;
            return false;
        }
        // 用CountDownLatch等待Firebase异步上传完成
        final boolean[] uploadSuccess = {false};
        final CountDownLatch latch = new CountDownLatch(1);
        
        StorageReference filepath = mStorage.child("My_Images").child(catValue).child(mImageuri.getLastPathSegment());
        filepath.putFile(mImageuri).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
            @Override
            public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
                uploadSuccess[0] = true;
                latch.countDown();
            }
        }).addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                uploadSuccess[0] = false;
                latch.countDown();
            }
        });
        
        try {
            latch.await(); // 等待上传结果
        } catch (InterruptedException e) {
            e.printStackTrace();
            return false;
        }
        return uploadSuccess[0];
    }

    @Override
    protected void onPostExecute(Boolean uploadSuccess) {
        super.onPostExecute(uploadSuccess);
        showProgressDialog(false);
        
        if (!isNetworkOk) {
            showAlert("Error", "Network is not available");
        } else if (uploadSuccess) {
            showAlert("Successful", "Upload complete.");
            clearAll();
        } else {
            showAlert("Failed", "Upload doesn't complete");
        }
    }

    @Override
    protected void onCancelled() {
        super.onCancelled();
        // 任务取消时也要关闭ProgressDialog,避免泄漏
        showProgressDialog(false);
    }
}

3. 修复Dialog的Context生命周期问题

修改showAlert方法,确保Fragment还依附在Activity上时才弹Dialog,避免Context失效:

private void showAlert(String title, String message) {
    // 检查Fragment是否还和Activity绑定,防止Context失效
    if (!isAdded()) return;
    
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    builder.setTitle(title);
    builder.setMessage(message);
    builder.setNeutralButton("Dismiss", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            dialog.dismiss();
        }
    });
    AlertDialog dialog = builder.create();
    dialog.show();
}

同时,在Fragment的onDestroyView方法里关闭ProgressDialog,避免内存泄漏:

@Override
public void onDestroyView() {
    super.onDestroyView();
    if (mProgress != null && mProgress.isShowing()) {
        mProgress.dismiss();
        mProgress = null;
    }
}

4. 额外优化:替换AsyncTask(可选)

因为AsyncTask在Android 11已经被废弃,推荐改用Java的ExecutorService来实现后台任务,这里给你一个简化示例:

mSbtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        if (!isFormValid()) {
            showAlert("Error", "Please fill all the details");
            return;
        }
        showProgressDialog(true);
        
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Handler handler = new Handler(Looper.getMainLooper());
        
        executor.execute(() -> {
            final boolean isNetworkOk = hasInternetAccess(getContext());
            boolean uploadSuccess = false;
            
            if (isNetworkOk) {
                String catValue = mcategory.getSelectedItem().toString();
                final CountDownLatch latch = new CountDownLatch(1);
                
                StorageReference filepath = mStorage.child("My_Images").child(catValue).child(mImageuri.getLastPathSegment());
                filepath.putFile(mImageuri).addOnSuccessListener(taskSnapshot -> {
                    uploadSuccess = true;
                    latch.countDown();
                }).addOnFailureListener(e -> {
                    uploadSuccess = false;
                    latch.countDown();
                });
                
                try {
                    latch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            
            // 回到UI线程更新状态
            handler.post(() -> {
                showProgressDialog(false);
                if (!isNetworkOk) {
                    showAlert("Error", "Network is not available");
                } else if (uploadSuccess) {
                    showAlert("Successful", "Upload complete.");
                    clearAll();
                } else {
                    showAlert("Failed", "Upload doesn't complete");
                }
            });
        });
    }
});

总结

这些修改主要解决了三个核心问题:

  • 避免在后台线程操作UI
  • 确保Dialog使用的Context是有效的,并且在Fragment销毁时清理Dialog
  • 修正了网络检查的逻辑错误

按照上面的步骤修改后,你的WindowLeaked错误应该就能解决了,同时上传逻辑也会更稳定。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:29:52