Firestore查询执行两次致Activity重复创建问题求助
解决Firestore监听器回调两次导致Activity重复创建的问题
问题原因
你碰到的这个情况是Firestore addSnapshotListener的默认机制导致的:这个实时监听器会先触发一次本地缓存的查询结果(如果之前有过相同查询的缓存数据),然后再触发一次从服务器拉取的最新数据快照。所以哪怕你的firebase_dynamicview()只调用一次,onEvent回调也会执行两次,日志2222222222自然打印两次,最终重复调用userprofile()导致Activity被多次创建。
解决方案
根据你的业务需求,分两种场景给出解决办法:
场景1:不需要实时监听数据(仅一次性加载)
如果你的业务只需要一次性获取员工数据,不需要实时同步更新,直接把addSnapshotListener换成一次性查询的get()方法即可,它只会执行一次回调:
private void firebase_dynamicview() { shared_menueditor.clear().commit(); shared_menueditor.putString("success", "failure"); shared_menueditor.apply(); Log.d("gettingcall","11111111111 "); db = FirebaseFirestore.getInstance(); Query query = db.collection("employees"); // 替换成一次性get查询 query.whereEqualTo("UID", sharedPref.getString("userId",null)) .get() .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() { @Override public void onComplete(@NonNull Task<QuerySnapshot> task) { if (task.isSuccessful()) { QuerySnapshot documentSnapshots = task.getResult(); if (documentSnapshots.isEmpty()){ closeSplash(); return; } Log.d("gettingcall","2222222222 "); // 以下是你原来的解析逻辑 for (DocumentChange documentChange : documentSnapshots.getDocumentChanges()) { Map<String, Object> forms =null; if (documentChange.getDocument().getData().get("mobileMenu") != null) { try { forms= (Map<String, Object>)documentChange.getDocument().getData().get("mobileMenu"); } catch (Exception e1) { Log.e("Execeptionmenus","** "+e1.toString()); e1.printStackTrace(); } } if (forms!= null) { // ... 原来的遍历解析逻辑 } } shared_menueditor.apply(); editor.apply(); Log.d("calllliiiiiiing******","spalsh"); userprofile(); } else { // 处理查询失败的情况 Log.e("FirestoreError", task.getException().getMessage()); closeSplash(); } } }); }
场景2:需要实时监听数据,但避免重复启动Activity
如果业务需要实时同步员工数据的变化,但不想重复执行userprofile()启动Activity,可以通过以下两种方式处理:
- 方式1:跳过本地缓存的初始触发
判断快照是否来自本地缓存,只处理服务器返回的最新数据:
// 在你的addSnapshotListener回调里添加判断 @Override public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) { if (e != null) { Log.e("FirestoreError", e.getMessage()); return; } // 跳过本地缓存的快照,只处理服务器返回的数据 if (documentSnapshots.getMetadata().isFromCache()) { return; } // 以下是你原来的逻辑 if (documentSnapshots.isEmpty()){ closeSplash(); } Log.d("gettingcall","2222222222 "); // ... 解析数据 shared_menueditor.apply(); editor.apply(); userprofile(); }
- 方式2:用标记变量控制只执行一次初始化
在类中定义一个成员变量标记是否已经启动过Activity,确保userprofile()只执行一次:
// 在你的Activity类中添加成员变量 private boolean isProfileInitiated = false; // 然后在回调中判断 @Override public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) { if (e != null) { Log.e("FirestoreError", e.getMessage()); return; } if (documentSnapshots.isEmpty()){ closeSplash(); } Log.d("gettingcall","2222222222 "); // ... 先完成所有数据解析和SharedPreferences存储逻辑 shared_menueditor.apply(); editor.apply(); // 只有第一次才启动Activity if (!isProfileInitiated) { userprofile(); isProfileInitiated = true; } }
额外优化建议
你的代码中多次调用shared_menueditor.apply()和commit(),其实只需要在所有putString()操作完成后,调用一次apply()(异步,推荐)或者commit()(同步)即可,这样能减少不必要的IO操作,提升性能。
内容的提问来源于stack exchange,提问作者Gowthaman M
相关产品推荐
相关产品推荐

