启用Firebase离线持久化后,从推送打开应用DataSnapshot值为null?
从你描述的现象和代码来看,这个问题主要和Firebase磁盘持久化下的本地缓存同步逻辑以及你使用的查询方式、API调用时机有关,具体分析和解决办法如下:
1. addListenerForSingleValueEvent 在持久化模式下的行为限制
当启用磁盘持久化时,addListenerForSingleValueEvent 会优先返回本地缓存中的数据,不会等待与服务器完成同步。如果后续从推送打开应用时,本地缓存中没有对应的 msgID 数据(比如首次打开后缓存未正确更新,或者应用启动时同步还未完成),就会返回 null。而禁用持久化后,每次都会直接从服务器拉取数据,所以不会出现这个问题。
2. 不必要的 orderByValue() 可能导致数据结构异常
你的查询代码是:
mQueryMessg = mRefMessages.child(msgID).orderByValue() ;
如果 msgID 对应的节点是单个消息对象(包含 Key_Type 等字段),orderByValue() 会把该对象的所有子节点按值排序后以列表形式返回,而不是返回原对象本身。这会导致 dataSnapshot 的结构和你预期的不一致:首次打开时服务器同步的数据可能还能正确解析,但后续本地缓存的排序后数据可能无法匹配 hasChild(AppConstants.Key_Type) 的判断,看起来像是 null。
3. keepSynced(true) 的调用时机过晚
你在 startIntent 方法中才调用 mRefMessages.keepSynced(true),但此时应用刚从推送启动,Firebase 还没来得及完成数据同步。keepSynced(true) 是告诉 Firebase 持续同步该引用的数据,但这个同步需要时间,而 addListenerForSingleValueEvent 已经触发并返回了本地缓存的旧数据。
解决方案
方案一:修正查询逻辑,移除不必要的 orderByValue()
如果 msgID 对应的是单个消息节点,直接查询该节点即可,不需要排序:
mQueryMessg = mRefMessages.child(msgID); // 移除orderByValue()
这样获取的 DataSnapshot 就是完整的消息对象,能正确判断 hasChild(AppConstants.Key_Type)。
方案二:改用 addValueEventListener 替代 addListenerForSingleValueEvent
addValueEventListener 会在本地缓存更新(与服务器同步完成)时再次触发回调,确保你能拿到最新的数据。记得在获取到数据后移除监听器,避免内存泄漏:
ValueEventListener listener = new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { Log.d(TAG, "data snap Shot = " + dataSnapshot.getValue()); if (dataSnapshot.hasChild(AppConstants.Key_Type)) { Toast.makeText(ActivityMessages.this, "have child type == true" , Toast.LENGTH_SHORT).show(); // 处理完数据后移除监听器 mQueryMessg.removeEventListener(this); //open message view //some code . . . } } @Override public void onCancelled(DatabaseError databaseError) { Log.d(TAG, "data base eror " + databaseError.toString()); mQueryMessg.removeEventListener(this); } }; mQueryMessg.addValueEventListener(listener);
方案三:提前设置 keepSynced(true)
在应用启动时(比如 Application 类的 onCreate 方法中)就对消息引用设置 keepSynced(true),这样应用启动后就开始同步数据,当收到推送打开应用时,本地缓存已经有最新数据了:
// 在Application类中 FirebaseDatabase.getInstance().setPersistenceEnabled(true); DatabaseReference mRefMessages = FirebaseDatabase.getInstance().getReference(AppConstants.TableMessages.Table_name); mRefMessages.keepSynced(true);
方案四:升级Firebase数据库版本
你当前使用的 firebase-database:11.0.2 是2017年的旧版本,存在一些已知的持久化相关bug。建议升级到较新的稳定版本(比如 19.7.0,注意和 com.google.gms:google-services 版本匹配,google-services至少要4.3.0以上),新版本修复了很多缓存同步的问题。
内容的提问来源于stack exchange,提问作者wajde

