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

如何同步Firebase请求函数?Java代码遇日志不打印、页面冻结问题

问题分析与解决方案

核心问题

你的代码导致Activity冻结、日志无输出的原因如下:

  1. 主线程被阻塞死锁:如果getAllClothes()是在主线程调用的,wait()会直接挂起主线程。而Firebase的onComplete回调默认在主线程执行,主线程被挂起后,回调根本无法触发,notifyAll()永远不会被调用,线程一直处于等待状态,所以"signed in"和"moved"的日志无法输出,Activity也因主线程阻塞而冻结。
  2. 异步转同步逻辑错误:用synchronized+wait()/notifyAll()强行转换Firebase异步任务的方式本身存在逻辑漏洞,当回调线程与等待线程为同一个(主线程)时必然出现死锁。另外onSuccessTask用法错误,它用于Task链式处理,而非监听成功回调。

正确解决思路

Firebase API为异步设计,绝对不能在主线程用阻塞方式等待异步结果,推荐两种可行方案:

方案1:使用回调接口(传统异步实现)

定义回调接口,在Firebase任务完成后通过回调返回结果,完全遵循异步设计:

// 定义回调接口
public interface ClothesCallback {
    void onSuccess(ArrayList<Clothe> clothes);
    void onFailure(Exception e);
}

// 异步获取衣物数据的方法
public void getAllClothes(ClothesCallback callback) {
    mAuth.signInWithEmailAndPassword("xxx@gmail.com", "xxx")
        .addOnCompleteListener(task -> {
            if (task.isSuccessful()) {
                Log.d("tag", "signed in");
                String uid = Objects.requireNonNull(mAuth.getCurrentUser()).getUid();
                reference.child(uid).get()
                    .addOnSuccessListener(dataSnapshot -> {
                        Log.d("tag", "ready?");
                        Log.d("tag", dataSnapshot.toString());
                        // 解析DataSnapshot为衣物列表
                        ArrayList<Clothe> clothes = new ArrayList<>();
                        for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                            Clothe clothe = snapshot.getValue(Clothe.class);
                            clothes.add(clothe);
                        }
                        callback.onSuccess(clothes);
                    })
                    .addOnFailureListener(e -> callback.onFailure(e));
            } else {
                callback.onFailure(task.getException());
            }
        });
}

调用示例(在Activity中处理结果):

clotheFirebaseHelper.getAllClothes(new ClothesCallback() {
    @Override
    public void onSuccess(ArrayList<Clothe> clothes) {
        // 处理获取到的衣物数据
    }

    @Override
    public void onFailure(Exception e) {
        // 处理登录或查询失败的情况
    }
});

方案2:使用Coroutine(兼容Java/Kotlin)

如果项目支持Coroutine,可以用Tasks.await()在协程中同步等待结果,但必须在非主线程的协程上下文中执行,避免阻塞UI:

// 需确保项目引入Coroutine相关依赖
public void getAllClothes(Continuation<ArrayList<Clothe>> continuation) {
    CoroutineScope(Dispatchers.IO).launch(() -> {
        try {
            // 在IO线程等待登录完成
            Tasks.await(mAuth.signInWithEmailAndPassword("xxx@gmail.com", "xxx"));
            Log.d("tag", "signed in");
            
            String uid = Objects.requireNonNull(mAuth.getCurrentUser()).getUid();
            // 等待数据库查询完成
            DataSnapshot dataSnapshot = Tasks.await(reference.child(uid).get());
            Log.d("tag", "ready?");
            Log.d("tag", dataSnapshot.toString());
            
            // 解析数据为衣物列表
            ArrayList<Clothe> clothes = new ArrayList<>();
            for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                Clothe clothe = snapshot.getValue(Clothe.class);
                clothes.add(clothe);
            }
            
            // 切换回主线程返回结果
            CoroutineScope(Dispatchers.Main).launch(() -> {
                continuation.resumeWith(Result.success(clothes));
            });
        } catch (Exception e) {
            CoroutineScope(Dispatchers.Main).launch(() -> {
                continuation.resumeWith(Result.failure(e));
            });
        }
    });
}

关键注意事项

  • 禁止在Android主线程调用wait()、Tasks.await()等阻塞方法,会直接触发ANR(应用无响应)。
  • Firebase异步回调默认在主线程执行,若需处理大量数据解析等耗时操作,需手动切换到子线程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 05:00:33