如何读取Firebase Realtime Database中包含List的自定义对象?
错误原因
Firebase Realtime Database 反序列化自定义类时,要求类的字段、get/set 方法完全符合 JavaBean 规范,且泛型声明完整。你遇到的报错核心原因是 HostPlayer 类中 hints 字段对应的 get、set 方法没有明确指定泛型为 String,Java 泛型擦除机制导致 Firebase 无法识别 List 内存储的数据类型,因此抛出需要使用 GenericTypeIndicator 的异常。
解决方案
方案1:修复 HostPlayer 的泛型声明(优先尝试)
修改 HostPlayer 类的字段访问权限、get/set 方法的泛型声明,符合 Firebase 反序列化要求,修改后的完整类代码如下:
public class HostPlayer { private String hostName; private String photoUrl; private String primaryHint; private List<String> hints = new ArrayList<String>(); private int numHints; private int numTime; private boolean inProgress = false; public HostPlayer() { } public HostPlayer(String hostName, int numHints, int numTime, List<String> hints, String primaryHint, String photoUrl) { this.hostName = hostName; this.numHints = numHints; this.numTime = numTime; this.hints = hints; this.primaryHint = primaryHint; this.photoUrl = photoUrl; this.inProgress = false; } public void setPhotoUrl(String photoUrl) {this.photoUrl = photoUrl;} public void setPrimaryHint(String primaryHint) {this.primaryHint = primaryHint;} // 修正setHints的泛型声明 public void setHints(List<String> hints) {this.hints = hints;} public String getHostName() {return this.hostName;} public int getNumHints() {return this.numHints;} public int getNumTime() {return this.numTime;} public String getPrimaryHint() {return this.primaryHint;} // 修正getHints的泛型声明 public List<String> getHints() {return this.hints;} public void setInProgress() {this.inProgress = true;} public boolean getInProgress() {return this.inProgress;} }
修改完成后直接运行测试,绝大多数场景下改完泛型声明就可以解决报错。
方案2:手动解析数据(方案1无效时使用)
如果方案1修改后仍然报错,可以放弃自动反序列化,手动读取 DataSnapshot 的字段构造 HostPlayer 对象,步骤如下:
- 把
JoinGame类中的适配器改为成员变量,方便后续刷新列表 - 在
onChildAdded方法中手动读取各字段,用GenericTypeIndicator读取hints列表 - 新增数据后调用
notifyDataSetChanged刷新 ListView
修改后的 JoinGame 完整代码如下:
public class JoinGame extends AppCompatActivity { ListView lv; ArrayAdapter<HostPlayer> hostPlayerArrayAdapter; // 适配器改为成员变量 private FirebaseDatabase pFireBaseDatabase; private DatabaseReference pDatabaseReference; private ChildEventListener pChildEventListener; List<HostPlayer> hostPlayerList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_join_game); pFireBaseDatabase = FirebaseDatabase.getInstance(); pDatabaseReference = pFireBaseDatabase.getReference().child("hosts"); pChildEventListener = new ChildEventListener() { @Override public void onChildAdded(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) { // 手动读取各基础字段 String hostName = snapshot.child("hostName").getValue(String.class); int numHints = snapshot.child("numHints").getValue(Integer.class) != null ? snapshot.child("numHints").getValue(Integer.class) : 0; int numTime = snapshot.child("numTime").getValue(Integer.class) != null ? snapshot.child("numTime").getValue(Integer.class) : 0; String primaryHint = snapshot.child("primaryHint").getValue(String.class); String photoUrl = snapshot.child("photoUrl").getValue(String.class); boolean inProgress = snapshot.child("inProgress").getValue(Boolean.class) != null && snapshot.child("inProgress").getValue(Boolean.class); // 用GenericTypeIndicator读取泛型List GenericTypeIndicator<List<String>> hintsType = new GenericTypeIndicator<List<String>>() {}; List<String> hints = snapshot.child("hints").getValue(hintsType); // 构造HostPlayer对象 HostPlayer hostPlayer = new HostPlayer(hostName, numHints, numTime, hints, primaryHint, photoUrl); if (inProgress) { hostPlayer.setInProgress(); } hostPlayerList.add(hostPlayer); // 刷新列表 hostPlayerArrayAdapter.notifyDataSetChanged(); } @Override public void onCancelled(@NonNull DatabaseError error) { } }; pDatabaseReference.addChildEventListener(pChildEventListener); lv = findViewById(R.id.listView_hosts); hostPlayerArrayAdapter = new ArrayAdapter<HostPlayer>( this, android.R.layout.simple_list_item_1, hostPlayerList ); lv.setAdapter(hostPlayerArrayAdapter); } }
内容的提问来源于stack exchange,提问作者V1V3
相关产品推荐
相关产品推荐

