简体   繁体   中英

Android Firebase Database keeps updating value and eventually app crash

Whenever I updated the value 1st time, it works fine. But whenever I updated it again within a minute. The problem started, Firebase keeps updating, and as a result the apps crash. Can anyone help?

private void UpdatedStatus(String roomkey,String rents_not){

   reference= FirebaseDatabase.getInstance().getReference("Room");
   Query query = reference.orderByChild("Roomkey").equalTo(roomkey);


   if (reference != null) {

       query.addValueEventListener(new ValueEventListener() {
           @Override
           public void onDataChange(@NonNull DataSnapshot snapshot) {

               for (DataSnapshot dataSnapshot1 : snapshot.getChildren())
               {
                   String childPath=dataSnapshot1.getKey();
                   referenceUpdated = FirebaseDatabase
                           .getInstance().getReference("Room").child(childPath);


                   HashMap<String, Object> map = new HashMap<>();
                   map.put("Status", ""+rents_not);
                   
                   referenceUpdated.updateChildren(map).addOnCompleteListener(new OnCompleteListener<Void>() {
                       @Override
                       public void onComplete(@NonNull Task<Void> task) {
                           Toast.makeText(getApplicationContext(), "Updated", Toast.LENGTH_SHORT).show();
                           finish();
                           dialog.dismiss();
                       }
                   });
               }
           }

           @Override
           public void onCancelled(@NonNull DatabaseError error) {

           }
       });
   }

}

Firebase 截图:

As auspicious99 answered , since you update the same node that your query is listening for, your own call to updateChildren ends up triggering onDataChange again. And since you then update the node again, it triggers again, and again, and again... etc.

There are a few solutions for this:

  1. Use addListenerForSingleValueEvent , which triggers only once and stops listener after that. This is the simplest change, as you can just change

    query.addValueEventListener(new ValueEventListener() {

    to

    query.addListenerForSingleValueEvent(new ValueEventListener() {
  2. You can also choose to detect whether the Status field is already set to rents_not and skip the update if it is. Inside the loop in onDataChange , you can do this with:

     public void onDataChange(@NonNull DataSnapshot snapshot) { for (DataSnapshot houseSnapshot: snapshot.getChildren()) { DataSnapshot statusSnapshot = houseSnapshot.child("Status"); String status = statusSnapshot.getValue(String.class); if (!rents_not.equals(status)) { statusSnapshot.getReference().setValue(rents_not).addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { Toast.makeText(getApplicationContext(), "Updated", Toast.LENGTH_SHORT).show(); finish(); dialog.dismiss(); } }); } }

    You'll note that I've updated the code here a bit, since you can get the reference from the snapshot, and perform the update at the lower level.

These will both fix your current problem, so pick whichever works for the use-case. But since your new value of the Status node depends on its current value, consider using a transaction to update it, to eliminate the risk that two users will both post conflicting updates.

It looks like you may have an unintended loop, as inside your onDataChange , you are doing referenceUpdated.updateChildren(map) , which changes data in your firebase database. This triggers onDataChange again, and so on.

You could break the circle by setting a Boolean/flag and checking it as needed.

In your current code, you do the following:

  1. Set up a query for rooms with "Roomkey" == roomkey .
  2. Attach a listener for updates on rooms that match the query
  3. Upon getting results, set each matching room's status to rents_not

When you call UpdatedStatus("room1", "AVAILABLE") , everything works "fine" (the room "room1" is changed to AVAILABLE , then the room is changed to AVAILABLE again and finish.). Because you used addValueEventListener to listen for updates to the matching rooms, your code gets rerun when the data changes. This is why I said the status is changed twice.

Next, when you call UpdatedStatus("room1", "RENTED") , you experience the crash. After calling UpdatedStatus the second time, the status is changed to "RENTED", which fires both of the listeners from UpdatedStatus("room1", "AVAILABLE") and UpdatedStatus("room1", "RENTED") , constantly switching between "RENTED" and "AVAILABLE" forever.

You can fix this loop (and the double write) by using addSingleValueEventListener() instead. This will execute and read from the query only once .

FirebaseDatabase mDatabase = FirebaseDatabase.getInstance();
DatabaseReference mRoomsRef = mDatabase.getReference("Room");

private void UpdatedStatus(String roomkey,String rents_not){

   Query roomQuery = mRoomsRef.orderByChild("Roomkey").equalTo(roomkey);

   roomQuery.addSingleValueEventListener(new ValueEventListener() {
       @Override
       public void onDataChange(@NonNull DataSnapshot snapshot) {
           for (DataSnapshot childSnapshot: snapshot.getChildren())
           {
               String childPath = childSnapshot.getKey();
               DatabaseReference childRef = childSnapshot.getReference();

               HashMap<String, Object> map = new HashMap<>();
               map.put("Status", ""+rents_not);

               childRef.updateChildren(map)
                   .addOnCompleteListener(new OnCompleteListener<Void>() {
                       @Override
                       public void onComplete(@NonNull Task<Void> task) {
                           Toast.makeText(getApplicationContext(), "Updated", Toast.LENGTH_SHORT).show();
                           finish();
                           dialog.dismiss();
                       }
                   });
               }
           }

           @Override
           public void onCancelled(@NonNull DatabaseError error) {
               Log.e(TAG, "Failed to get rooms for key: " + roomkey + "; " + error.getMessage());
           }
       });
   }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM