如何解决Android的android.view.WindowLeaked错误及图片上传崩溃问题
解决Android View.WindowLeaked错误及AsyncTask网络检查问题
首先,咱们来拆解你遇到的android.view.WindowLeaked错误——这个问题主要出在三个核心点:后台线程直接操作UI、Dialog依附的Context生命周期不匹配,再加上你的网络检查逻辑存在反转错误,咱们一步步来修复:
错误原因分析
- 后台线程操作UI:你的
UploadingTask.doInBackground里直接调用了showAlert(),而doInBackground是在后台线程执行的,Android严格禁止在非UI线程更新UI(包括弹出Dialog),这不仅会引发异常,还会导致窗口泄漏。 - Context生命周期问题:你用
getContext()创建AlertDialog和ProgressDialog,但如果Fragment已经和Activity分离(比如用户切换了页面),这个Context就会失效,Dialog还挂着就会触发WindowLeaked。 - 网络检查逻辑反转:看你的
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
相关产品推荐
相关产品推荐

