如何同步Firebase请求函数?Java代码遇日志不打印、页面冻结问题
问题分析与解决方案
核心问题
你的代码导致Activity冻结、日志无输出的原因如下:
- 主线程被阻塞死锁:如果
getAllClothes()是在主线程调用的,wait()会直接挂起主线程。而Firebase的onComplete回调默认在主线程执行,主线程被挂起后,回调根本无法触发,notifyAll()永远不会被调用,线程一直处于等待状态,所以"signed in"和"moved"的日志无法输出,Activity也因主线程阻塞而冻结。 - 异步转同步逻辑错误:用
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
相关产品推荐
相关产品推荐

