Firebase Realtime Database结合LiveData/ViewModel实时更新失效问题排查
Firebase Realtime Database结合ViewModel/LiveData监听问题排查
问题概述
开发了一个DialogFragment,结合Firebase Realtime Database、ViewModel与LiveData实现数据库变化的持续监听,目前遇到两个问题:
onChanged方法仅触发一次,修改数据库数据后不再调用;dataSnapshot始终为null,但ViewModel中的查询语句理论上应返回数据;
同时疑问:Firebase Realtime Database是否支持实时数据更新?
DialogFragment实现代码
public class DialogFR_LevelEnd extends DialogFragment implements View.OnClickListener { public DialoagFragmentLevelEndingBinding binding; private double co2SavingsScore; private RecyclerView recyclerView_HighScore; private RV_Adapter_Highscore adapter_HighScore; private RecyclerView.LayoutManager layoutManager_HighScore; private ArrayList<RV_Item_Highscore> arrayList_HighScore; private int pastDaysForDisplayingScores = 0; private int currentLevel; //Firebase components private ViewModel_DialogFR_LevelEnd viewModel; LiveData<DataSnapshot> liveData; DataSnapshot currentDataSnapShotFromFirebase; DatabaseReference rootRef_Firebase; String FIREBASE_CO_2_SCORE = "co2_score"; String FIREBASE_DATE = "date"; String FIREBASE_DATE_IN_MILLISECONDS = "date_in_milliseconds"; String FIREBASE_LEVEL = "level"; String FIREBASE_NAME = "name"; public static DialogFR_LevelEnd newInstance(double co2SavingsScore, double neededCO2SavingScore, int currentLevel) { DialogFR_LevelEnd fragment = new DialogFR_LevelEnd(); Bundle args = new Bundle(); args.putDouble("co2SavingsScore", co2SavingsScore); args.putDouble("neededCO2SavingScore", neededCO2SavingScore); args.putInt("currentLevel", currentLevel); fragment.setArguments(args); return fragment; } public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { binding = DialoagFragmentLevelEndingBinding.inflate(inflater, container, false); double co2SavingsScore = getArguments().getDouble("co2SavingsScore", 0.0); double neededCO2SavingsScore = getArguments().getDouble("neededCO2SavingScore", 0.0); this.currentLevel = getArguments().getInt("currentLevel", 0); binding.textViewLevelFinishedMessageCO2.setText("You needed to save " + neededCO2SavingsScore+ "g of CO2 and you got " + co2SavingsScore + "g"); //Check if the level was successfull if (co2SavingsScore>= neededCO2SavingsScore) { //Level passed binding.imageViewCloseSymbol.setVisibility(View.INVISIBLE); } else { //Level not passed binding.imageViewCheckmark.setVisibility(View.INVISIBLE); binding.imageViewRepeatSymbol.setClickable(false); binding.imageViewRepeatSymbol.setAlpha(0.5f); binding.textViewNextLevel.setAlpha(0.5f); } //Initialize highScore array list and the RV adapter arrayList_HighScore = new ArrayList<RV_Item_Highscore>(); adapter_HighScore = new RV_Adapter_Highscore(arrayList_HighScore); //Firebase components String FIREBASE_URL = "https://heatinggamehighscores-default-rtdb.europe-west1.firebasedatabase.app/"; if (FirebaseApp.getApps(getContext()).isEmpty()) { FirebaseOptions options = new FirebaseOptions.Builder() .setApplicationId("1:248974012708:android:3f93234ba2066ed41643b1") .setDatabaseUrl(FIREBASE_URL) .build(); FirebaseApp.initializeApp(getContext(), options); } rootRef_Firebase = FirebaseDatabase.getInstance(FIREBASE_URL).getReference(); rootRef_Firebase.keepSynced(true); // Initialize ViewModel viewModel = new ViewModelProvider(this).get(ViewModel_DialogFR_LevelEnd.class); viewModel.setFirebaseNodeLevel(currentLevel); viewModel.setPastTimeMillis(pastDaysForDisplayingScores); liveData = viewModel.getData(); Log.e("Tag_Dialog", "DialogFragment: ViewModel Get Data Called"); liveData.observe(this, new Observer<DataSnapshot>() { @Override public void onChanged(@Nullable DataSnapshot dataSnapshot) { Log.e("Tag_Dialog", "DialogFragment: onChanged Called"); if (dataSnapshot != null) { Log.e("Tag_Dialog", "DialogFragment: DataSnapshot !=null"); arrayList_HighScore.clear(); for(DataSnapshot ds: dataSnapshot.getChildren()) { Log.e("Tag_Dialog", "DialogFragment: DataSnapshot iteration"); if (ds != null) { String name = ""; int co2Score = 0; String date = ""; int level = 0; int position = 0; // update the UI here with values in the snapshot if(ds.child(FIREBASE_NAME).getValue(String.class)!=null) { name = ds.child(FIREBASE_NAME).getValue(String.class); } if(ds.child(FIREBASE_CO_2_SCORE).getValue(Integer.class)!=null) { co2Score = ds.child(FIREBASE_CO_2_SCORE).getValue(Integer.class); } if(ds.child(FIREBASE_LEVEL).getValue(Integer.class)!=null) { level = ds.child(FIREBASE_LEVEL).getValue(Integer.class); } if(ds.child(FIREBASE_DATE).getValue(String.class)!=null) { date = ds.child(FIREBASE_DATE).getValue(String.class); } arrayList_HighScore.add(new RV_Item_Highscore(name, co2Score, date, level, position)); } } } }// end onChange }); //end observe buildRecyclerView(); return binding.getRoot(); } public void buildRecyclerView() { recyclerView_HighScore = binding.rvHighScoreStatisticsToBeDisplayed; recyclerView_HighScore.setHasFixedSize(true); layoutManager_HighScore = new LinearLayoutManager(this.getContext()); recyclerView_HighScore.setLayoutManager(layoutManager_HighScore); recyclerView_HighScore.setAdapter(adapter_HighScore); } public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); } @Override public void onClick(View view) { if (view instanceof ToggleButton) { if (binding.tbuttonHighScoreLastWeek.isChecked()) { this.pastDaysForDisplayingScores = 7; } if (binding.tbuttonHighScoreLastMonth.isChecked()) { this.pastDaysForDisplayingScores = 30; } if (binding.tbuttonHighScoreOverall.isChecked()) { this.pastDaysForDisplayingScores = 10000; } viewModel.setPastTimeMillis(pastDaysForDisplayingScores); } } }
ViewModel代码
public class ViewModel_DialogFR_LevelEnd extends ViewModel { public static final String FIREBASE_URL = "https://heatinggamehighscores-default-rtdb.europe-west1.firebasedatabase.app/"; public static String FIREBASE_NODE_LEVEL = ""; public static final String FIREBASE_DATE_IN_MILLISECONDS = "date_in_milliseconds"; private MutableLiveData<DataSnapshot> data = new MutableLiveData<DataSnapshot>(); private static long pastTimeMillis; public void setFirebaseNodeLevel(int levelNumber) { FIREBASE_NODE_LEVEL = "level_" + levelNumber; } public void setPastTimeMillis (int pastDaysForDisplayingScores) { pastTimeMillis = System.currentTimeMillis() - (long) (pastDaysForDisplayingScores * 24L * 60 * 60 * 1000); data.postValue(null); } public LiveData<DataSnapshot> getData() { Log.e("Tag_Dialog", "ViewModel: getData called"); Query QUERY_DATE = FirebaseDatabase.getInstance(FIREBASE_URL).getReference().child(FIREBASE_NODE_LEVEL).orderByChild(FIREBASE_DATE_IN_MILLISECONDS).startAt(pastTimeMillis); Log.e("Tag_Dialog", "ViewModel: Query =" + QUERY_DATE); return data; } }
Firebase数据库结构
数据库根节点下包含多个以level_数字命名的节点(如level_1、level_2),每个level节点下有若干子节点,每个子节点包含以下字段:
co2_score:数值类型date:字符串类型(日期)date_in_milliseconds:数值类型(时间戳)level:数值类型name:字符串类型(用户名)
内容的提问来源于stack exchange,提问作者VanessaF
相关产品推荐
相关产品推荐

