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

如何解决Firebase按时间戳排序后地图标记自定义信息窗口显示旧数据的问题

问题根源
  • 嵌套查询逻辑错误:你在User Location的onChildAdded回调中监听了整个Users节点的子节点事件,这会导致每新增一个位置,就会把所有用户的数据都绑定到当前位置上生成标记,自然会先加载历史旧用户数据,再加载最新用户数据
  • 监听器重复注册:每次触发onChildAdded都会新增一个ChildEventListener,旧的监听器不会自动销毁,会多次重复触发回调生成冗余标记
  • 无标记映射管理:每次拿到数据就新增标记,没有对应UID更新已有标记的逻辑,导致同一位置出现多个重叠标记,点击时优先展示旧标记的内容
修复方案
  1. 直接通过位置节点的UID查询对应用户的单条数据,不要监听整个Users节点,User Location和Users两个节点的子节点key就是对应用户的UID,可以直接关联查询
  2. 提前初始化HashMap存储UID与标记的映射关系,避免重复添加标记,数据更新时直接更新对应标记的内容
  3. 全局只设置一次InfoWindowAdapter,不要在回调中重复设置
修复后核心代码

首先在Activity类中新增成员变量存储标记映射:

// 存储用户UID与对应地图标记的映射关系
private HashMap<String, Marker> uidMarkerMap = new HashMap<>();

修改后的onMapReady代码:

@Override
public void onMapReady(@NonNull GoogleMap googleMap) {
    mMap = googleMap;
    // 全局只设置一次信息窗口适配器
    mMap.setInfoWindowAdapter(new InfoWindowAdapter(Retrieve.this));

    DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
    DatabaseReference locationRef = rootRef.child("User Location");

    locationRef.orderByChild("timestamp").addChildEventListener(new ChildEventListener() {
        @Override
        public void onChildAdded(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {
            // 拿到当前位置对应用户的UID
            String uid = snapshot.getKey();
            LocationSend send = snapshot.getValue(LocationSend.class);
            LatLng location = new LatLng(send.getLatitude(), send.getLongitude());

            // 直接查询对应用户的单条数据,无需监听整个Users节点
            DatabaseReference userRef = rootRef.child("Users").child(uid);
            // 需要实时更新用户数据可以替换为addValueEventListener
            userRef.addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot) {
                    if (!snapshot.exists()) return;
                    String name = snapshot.child("name").getValue(String.class);
                    String emergencyType = snapshot.child("Emergency Type").getValue(String.class);
                    String alertLevel = snapshot.child("Alert Level").getValue(String.class);
                    
                    // 先判断该用户是否已有标记,有则更新,无则新增
                    Marker marker = uidMarkerMap.get(uid);
                    if (marker == null) {
                        marker = mMap.addMarker(new MarkerOptions().position(location)
                                .title(name)
                                .snippet(emergencyType + "/" + alertLevel));
                        uidMarkerMap.put(uid, marker);
                    } else {
                        marker.setPosition(location);
                        marker.setTitle(name);
                        marker.setSnippet(emergencyType + "/" + alertLevel);
                        // 如果当前标记的信息窗口正在展示,立即刷新内容
                        if (marker.isInfoWindowShown()) {
                            marker.showInfoWindow();
                        }
                    }
                    // 按需调整摄像头移动逻辑,不用每次加载数据都移动
                    mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(location, 14F));
                }

                @Override
                public void onCancelled(@NonNull DatabaseError error) {
                    Log.e("TAG", "加载用户数据失败", error.toException());
                }
            });
            notification();
        }

        @Override
        public void onChildChanged(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {
            // 位置更新时同步更新对应标记的位置
            String uid = snapshot.getKey();
            LocationSend send = snapshot.getValue(LocationSend.class);
            LatLng location = new LatLng(send.getLatitude(), send.getLongitude());
            Marker marker = uidMarkerMap.get(uid);
            if (marker != null) {
                marker.setPosition(location);
                if (marker.isInfoWindowShown()) {
                    marker.showInfoWindow();
                }
            }
            notification();
        }

        @Override
        public void onChildRemoved(@NonNull DataSnapshot snapshot) {
            // 用户位置删除时同步移除对应标记
            String uid = snapshot.getKey();
            Marker marker = uidMarkerMap.remove(uid);
            if (marker != null) {
                marker.remove();
            }
        }

        @Override
        public void onChildMoved(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {}

        @Override
        public void onCancelled(@NonNull DatabaseError error) {}
    });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 08:06:04