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

Firebase Realtime Database Android Java 竞态条件问题求助

解决Firebase异步请求竞态导致的用户列表顺序错乱问题

问题本质

当前代码中,getPlayerCount按Firebase内部字母顺序遍历用户ID,但每个setPlayerUsername是异步请求——登录用户的请求因本地缓存会优先完成,直接调用playerList.add(player)会打乱原本的顺序,同时依赖playerIndex关联后续setPlayerPicks也会因顺序错乱导致索引偏移。

解决方案1:固定列表位置,异步填充数据

先根据用户数量初始化playerList的结构,保证顺序与Firebase返回一致,异步请求完成后直接填充对应位置的数据,而非动态添加。

修改getPlayerCount方法:

private void getPlayerCount() {
    playerInPoolQuery = playerInPoolReference.child(poolId).orderByValue().equalTo(true);
    readPlayerCountData(playerInPoolQuery, new PicksActivity.AllPicksPlayerCountCallback() {

        @Override
        public void onSuccess(DataSnapshot dataSnapshot) {
            numPlayersInPool = (int) dataSnapshot.getChildrenCount();
            // 初始化列表,固定长度和顺序
            playerList = new ArrayList<>(numPlayersInPool);
            for (int i = 0; i < numPlayersInPool; i++) {
                playerList.add(null); // 占位,后续填充
            }

            int playerIndex = 0;
            for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                String userId = snapshot.getKey();
                setPlayerUsername(userId, playerIndex);
                playerIndex++;
            }
        }
    });
}

修改setPlayerUsername方法:

private void setPlayerUsername(final String userId, final int playerIndex) {
    userQuery = userReference.child(userId).child("username");
    readPlayerUsernameData(userQuery, new SurvivorAllPicksActivity.AllPicksPlayerUsernameCallback() {

        @Override
        public void onSuccess(DataSnapshot dataSnapshot) {
            String username = (String) dataSnapshot.getValue();
            AllPicks player = new AllPicks(username);
            
            // 直接填充到指定索引位置,保证顺序
            playerList.set(playerIndex, player);

            for (int i = 1; i <= currWeek; i++) {
                setPlayerPicks(userId, playerIndex, i);
            }
        }
    });
}

解决方案2:批量获取用户数据,减少异步请求

通过Firebase的in查询一次性获取所有需要的用户名,只需一次异步请求,完全避免竞态问题:

修改getPlayerCount方法:

private void getPlayerCount() {
    playerInPoolQuery = playerInPoolReference.child(poolId).orderByValue().equalTo(true);
    readPlayerCountData(playerInPoolQuery, new PicksActivity.AllPicksPlayerCountCallback() {

        @Override
        public void onSuccess(DataSnapshot dataSnapshot) {
            // 收集所有用户ID,保留原顺序
            List<String> userIdList = new ArrayList<>();
            for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                userIdList.add(snapshot.getKey());
            }
            numPlayersInPool = userIdList.size();

            // 批量查询所有用户的username
            userReference.orderByKey().in(userIdList).addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot userDataSnapshot) {
                    // 按原userIdList顺序构建playerList
                    for (String userId : userIdList) {
                        DataSnapshot userSnapshot = userDataSnapshot.child(userId);
                        String username = userSnapshot.child("username").getValue(String.class);
                        AllPicks player = new AllPicks(username);
                        playerList.add(player);
                    }

                    // 批量处理用户选择
                    for (int playerIndex = 0; playerIndex < userIdList.size(); playerIndex++) {
                        String userId = userIdList.get(playerIndex);
                        for (int i = 1; i <= currWeek; i++) {
                            setPlayerPicks(userId, playerIndex, i);
                        }
                    }
                }

                @Override
                public void onCancelled(DatabaseError databaseError) {
                    // 此处添加错误处理逻辑
                }
            });
        }
    });
}

注意事项

  • 方案1适合需要逐个处理用户的场景,方案2更高效,能减少网络请求次数。
  • 确保playerList在主线程操作(Firebase回调默认在主线程,无需额外处理)。
  • 使用方案1时,若需UI更新,可通过计数器跟踪已完成的请求数量,待全部完成后再刷新界面。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 06:22:37