Android Studio-Java+Firestore游戏用户分数存储与查询技术咨询
问题背景
我用Android Studio(Java)、Firestore和Firebase Auth开发一款包含3款游戏的应用,需求如下:
- 为每位用户存储每款游戏的最近5条分数+日期(格式DD/MM/YY)
- 存储每款游戏的最高分
当前采用单个users集合,User类定义如下:
private String name; private String email; private HashMap<Integer, Date> game1_Scores; private HashMap<Integer, Date> game2_Scores; private HashMap<Integer, Date> game3_Scores; private int game1_HighScore; private int game2_HighScore; private int game3_HighScore;
问题1:如何最优地用新分数覆盖最早的分数?
首先你当前用HashMap<Integer, Date>的方式有问题——HashMap是无序集合,没法维护分数的时间顺序,根本没法判断哪条是最早的。建议改成有序列表结构,比如自定义一个ScoreEntry类:
public class ScoreEntry { private int score; private Date date; // 空构造函数(Firestore序列化需要) public ScoreEntry() {} public ScoreEntry(int score, Date date) { this.score = score; this.date = date; } // Getter和Setter方法 }
然后把User类里的分数字段替换为:
private List<ScoreEntry> game1_Scores; private List<ScoreEntry> game2_Scores; private List<ScoreEntry> game3_Scores;
处理逻辑如下:
- 用户提交新分数时,先获取当前用户对应游戏的分数列表
- 如果列表长度已达5,删除其中日期最早的元素(遍历比较date字段即可)
- 将新的
ScoreEntry添加到列表末尾 - 同时检查新分数是否超过当前最高分,是则更新对应的
gameX_HighScore字段 - 用事务把更新后的User对象写回Firestore(避免并发更新冲突)
核心代码示例:
// 获取当前用户的文档引用 DocumentReference userDoc = FirebaseFirestore.getInstance() .collection("users") .document(FirebaseAuth.getInstance().getCurrentUser().getUid()); // 事务处理 userDoc.runTransaction(transaction -> { User user = transaction.get(userDoc).toObject(User.class); if (user == null) throw new Exception("用户不存在"); List<ScoreEntry> scores = user.getGame1_Scores(); if (scores == null) scores = new ArrayList<>(); ScoreEntry newEntry = new ScoreEntry(newScore, new Date()); // 已满5条则删除最早的 if (scores.size() >= 5) { scores.remove(findOldestEntryIndex(scores)); } scores.add(newEntry); // 更新最高分 if (newScore > user.getGame1_HighScore()) { user.setGame1_HighScore(newScore); } user.setGame1_Scores(scores); transaction.set(userDoc, user); return null; }); // 辅助方法:找到最早分数的索引 private int findOldestEntryIndex(List<ScoreEntry> entries) { int oldestIndex = 0; Date oldestDate = entries.get(0).getDate(); for (int i = 1; i < entries.size(); i++) { if (entries.get(i).getDate().before(oldestDate)) { oldestDate = entries.get(i).getDate(); oldestIndex = i; } } return oldestIndex; }
问题2:如何实现展示所有用户的高分排行榜(从高到低排序)?
方案1:直接查询users集合(适合小用户量)
如果用户数量不多,直接对users集合按对应游戏的最高分字段排序即可:
// 查询game1的全用户排行榜(取前10名) FirebaseFirestore.getInstance() .collection("users") .orderBy("game1_HighScore", Query.Direction.DESCENDING) .limit(10) .get() .addOnSuccessListener(querySnapshots -> { List<User> leaderboard = querySnapshots.toObjects(User.class); // 渲染排行榜UI });
注意:如果触发索引错误,需要到Firebase控制台为game1_HighScore创建单字段降序索引。
方案2:预生成排行榜(适合大用户量)
如果用户量很大,直接查询全集合会有性能问题,建议用Cloud Functions定期生成快照:
- 创建
leaderboards集合,每个游戏对应一个文档(比如game1_leaderboard) - 用定时触发的Cloud Functions查询所有用户的最高分,排序后写入对应文档
- 客户端直接读取预生成的排行榜数据,速度更快
问题3:是否应新建“scores”集合?如何关联分数与用户?
两种方案对比
当前单users集合方案 | 新建scores集合方案 |
|---|---|
| 查询用户自身分数/最高分速度快 | 支持全局分数统计(比如全用户分数分布、单游戏全分数排名) |
| 数据结构紧凑,适合小数据量 | 扩展性强,后续可添加关卡、成就等维度 |
| 无法高效做全局分数分析 | 需要额外维护用户的最近5条分数和最高分缓存 |
建议
如果仅需存储用户自身历史分数和全用户最高分,当前方案足够;如果未来需要复杂数据分析(比如分数趋势、全游戏分数排名),建议新建scores集合:
- 每个
scores文档包含字段:userId(关联用户ID)、gameId(1/2/3)、score、date - 保留
users集合中的gameX_Scores和gameX_HighScore作为缓存,避免频繁查询scores集合 - 关联方式:通过
userId字段过滤,查询scores集合时指定userId == 当前用户ID即可获取该用户的所有分数
附加问题:添加新User文档时如何控制字段顺序?
Firestore默认按字段名的ASCII码顺序在控制台显示字段,客户端读取时字段顺序不影响(因为是键值对)。如果一定要在控制台显示自定义顺序,有两种方法:
- 给字段名加前缀:比如把
name改成0_name,email改成1_email,game1_HighScore改成2_game1_HighScore,控制台会按前缀数字排序,但代码中需要同步修改字段名 - 使用@PropertyName注解:只能让代码字段名和Firestore字段名不一致,无法改变控制台显示顺序
注意:客户端读取数据时,字段顺序完全不影响业务逻辑,仅控制台展示有区别。
内容的提问来源于stack exchange,提问作者Ohad
相关产品推荐
相关产品推荐

