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

使用AlertDialog保存数据时Android应用崩溃问题求助

解决saveData()中AlertDialog导致程序崩溃的问题

我在向数据库保存数据时,想用AlertDialog显示进度条,但调用saveData()方法完成数据保存后程序崩溃。以下是运行出错的saveData()方法代码:

private void saveData() {

    if (isPickPhoto) {
        StorageReference mRef = FirebaseStorage.getInstance().getReference().child("ShopList Images")
                .child(imgUri.getLastPathSegment());
        AlertDialog.Builder builder = new AlertDialog.Builder(AddShopListActivity.this);
        builder.setCancelable(false);
        builder.setView(R.layout.progress_layout);
        AlertDialog dialog = builder.create();
        dialog.show();

        mRef.putFile(imgUri).addOnSuccessListener(taskSnapshot -> {
            Task<Uri> uriTask = taskSnapshot.getStorage().getDownloadUrl();
            while (!uriTask.isComplete()) ;
            Uri urlImage = uriTask.getResult();
            imageUrl = urlImage.toString();
            uploadData();
            dialog.dismiss();
        }).addOnFailureListener(e -> {
            dialog.dismiss();
        });
    } else {
        Toast.makeText(this, "请从相册选择一张图片再上传", Toast.LENGTH_SHORT).show();
    }
}

而用于更新数据的updateData()方法却能正常运行,代码如下:

private void updateData() {
    AlertDialog.Builder builder = new AlertDialog.Builder(AddShopListActivity.this);
    builder.setCancelable(false);
    builder.setView(R.layout.progress_layout);
    AlertDialog dialog = builder.create();
    dialog.show();

    if (isPhotoChanged) {

        StorageReference storageReference = FirebaseStorage.getInstance().getReference().child("ShopList Images").child(imgUri.getLastPathSegment());

        storageReference.putFile(imgUri).addOnSuccessListener(taskSnapshot -> {
            Task<Uri> uriTask = taskSnapshot.getStorage().getDownloadUrl();
            while (!uriTask.isComplete()) ;
            Uri urlImage = uriTask.getResult();
            imageUrl = urlImage.toString();
            uploadData();
            dialog.dismiss();
        }).addOnFailureListener(e -> {
            Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
        });
    } else {
        imageUrl = oldImageUrl;
        uploadData();
        dialog.dismiss();
    }
}

从LogCat的错误提示(java.lang.IllegalStateException: The specified child already exists)来看,崩溃核心不是AlertDialog,而是三个关键问题:


问题分析

  1. Firebase Database插入冲突:saveData是新增数据,但uploadData()里可能手动指定了已存在的节点Key,Firebase默认不允许直接覆盖新增节点,触发了子节点已存在的异常;而updateData是更新已有数据,本身就是覆盖操作,所以不会触发这个错误。
  2. 主线程被阻塞:用while (!uriTask.isComplete()) ;循环等待Uri,会卡死UI线程,不仅可能引发ANR,还会在Activity生命周期变化时(比如用户切后台)导致Dialog状态异常。
  3. Dialog Dismiss时机不安全:如果uploadData()内部调用了finish()关闭Activity,后续执行dialog.dismiss()时,Activity已经销毁,会触发IllegalStateException。

解决步骤

1. 修复Firebase数据插入逻辑

如果是新增数据,不要手动指定Key,用push()生成唯一Key:

// 替换uploadData()里的新增逻辑
DatabaseReference shopListsRef = FirebaseDatabase.getInstance().getReference("shoplists");
// 自动生成唯一节点Key
DatabaseReference newShopListRef = shopListsRef.push();
newShopListRef.setValue(yourShopListObject);

如果必须用自定义Key且允许覆盖,改用updateChildren():

Map<String, Object> updateMap = new HashMap<>();
updateMap.put(yourCustomKey, yourShopListObject);
shopListsRef.updateChildren(updateMap);

2. 替换阻塞式Uri获取

把死循环等待改成异步监听,避免卡死主线程:

mRef.putFile(imgUri).addOnSuccessListener(taskSnapshot -> {
    taskSnapshot.getStorage().getDownloadUrl().addOnCompleteListener(uriTask -> {
        if (uriTask.isSuccessful()) {
            imageUrl = uriTask.getResult().toString();
            uploadData();
            safeDismissDialog(dialog);
        } else {
            safeDismissDialog(dialog);
            Toast.makeText(AddShopListActivity.this, "获取图片链接失败", Toast.LENGTH_SHORT).show();
        }
    });
}).addOnFailureListener(e -> {
    safeDismissDialog(dialog);
    Toast.makeText(AddShopListActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
});

3. 安全关闭Dialog

封装一个方法,确保只有在Activity活跃且Dialog显示时才关闭:

private void safeDismissDialog(AlertDialog dialog) {
    if (!isFinishing() && !isDestroyed() && dialog != null && dialog.isShowing()) {
        dialog.dismiss();
    }
}

之后所有需要关闭Dialog的地方,都调用safeDismissDialog(dialog)代替直接dialog.dismiss()。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 15:45:33