Lokesh Tiwari
Lokesh Tiwari

Reputation: 10586

onDataChange is getting called twice in android Firebase

Here is My simple query for firebase data using timestamp in android app

Query recentStaticJobQuery = reference.child(AppConstants.WORKINDIA_JOBS)
                        .child(AppConstants.WORKINDIA_STATIC_JOBS)
                        .orderByChild(AppConstants.TIMESTAMP)
                        .startAt(lastStaticJobSyncTime); 
recentStaticJobQuery.addListenerForSingleValueEvent
(staticJobDownloadListener);


 ValueEventListener staticJobDownloadListener = new ValueEventListener() {
    @Override
    public void onDataChange(final DataSnapshot dataSnapshot) {
        Log.i("Firebase", "Called")
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        Log.i("Firebase", "onCancelled")
    }
};

How to avoid onDataChange to get called twice in android Firebase?

Upvotes: 18

Views: 6110

Answers (4)

Vikram Baliga
Vikram Baliga

Reputation: 504

I found out the reason for this issue.

Attaching and detaching the listeners were fine. Add listener getting called only once. But still child changes and value changes were getting called twice.

I noticed that the 2nd trigger was always about 320ms after the first one. The data was same in both the callbacks, except for the fields with ServerValue.TIMESTAMP. These fields also had about 320ms increase in the 2nd trigger.

The data I was writing to the firebase database was containing the ServerValue.TIMESTAMP as it stores the server's current timestamp in that field, and I was using it to filter the data.

On removing this field from the object I'm writing to database; the listeners are getting called only once.

Here is my code that caused 2 triggers:

My class

@Entity
class Route {
    @PrimaryKey
    var id: String = ""
        @Exclude
        get() {
            return field
        }
        @Exclude
        set(value) {
            field = value
        }
    ....
    ....
    @Ignore
    var z: Any? = null
}

In the viewModel

fun addRoute(routeName: String){
        viewModelScope.launch(Dispatchers.IO) {
            if(!repository.isRouteExists(routeName)){
                val route = Route()
                route.name = routeName
                route.addedBy = uid
                route.z = ServerValue.TIMESTAMP
                repository.insertRouteToFirebase(route)
            }
        }
    }

In the repository

fun insertRouteToFirebase(route: Route){
        val database = Firebase.database
        val myRef = database.getReference("routes")
        val routeId = myRef.push().key
        if(routeId != null){
            route.id = routeId
            myRef.child(routeId).setValue(route)
        }
    }

Logs when using ServerValue.TIMESTAMP (Notice the 300ms difference in log time and field z in data):

2022-09-11 11:38:05.880 23911-23911/com.slvt.vikku.sharadhamohotsav D/MY_FIREBASE: onDataChange: DataSnapshot { key = routes, value = {-NBf9NKGpmwWZDfp9uLp={addedBy=VLdrj1JbJhb1T9GXBm9WONhs5l03, name=new route , z=1662876484946}} }
2022-09-11 11:38:06.173 23911-23911/com.slvt.vikku.sharadhamohotsav D/MY_FIREBASE: onDataChange: DataSnapshot { key = routes, value = {-NBf9NKGpmwWZDfp9uLp={addedBy=VLdrj1JbJhb1T9GXBm9WONhs5l03, name=new route , z=1662876485282}} }

So the solution to this must be found out. This is just the cause of the issue. On changing

route.z = ServerValue.TIMESTAMP

to some other number like

 route.z = 791456

the listeners were getting called only once.

Upvotes: 0

Gourango Sutradhar
Gourango Sutradhar

Reputation: 1619

Replace your query by adding endAt() like below. It will help you.

Query recentStaticJobQuery = reference.child(AppConstants.WORKINDIA_JOBS)
                        .child(AppConstants.WORKINDIA_STATIC_JOBS)
                        .orderByChild(AppConstants.TIMESTAMP)
                        .startAt(lastStaticJobSyncTime).endAt(lastStaticJobSyncTime+"\uf8ff"); 

Upvotes: 0

Ajitesh Shukla
Ajitesh Shukla

Reputation: 131

There are 2 scenarios where this may happen:

  1. onDataChange is called twice in case you have enabled offline persistence. Once with the stale offline value and again with the updated value in case it has changed.

  2. onDataChange is called multiple times in case you have not removed the listener properly and are creating a new instance of your listener in your activity every time you open it.

Scenario 2 is easy to fix. You can maintain local references of your firebase reference and listener, than you can do a ref.removeListener(listener) in onDestroy of your Activity. Scenario 2 is difficult to fix and you have 2 possible remedies:

  1. Disable offline persistence in case you always want the updated latest value.
  2. Do a runnable.postDelayed(callbackRunnable, 3000); to wait for the latest value for 3 seconds before updating the views or whatever you want to update.

Upvotes: 9

KKumar Technologies
KKumar Technologies

Reputation: 267

Use SingleEventListener instead of ValueEventListener Like this

Firebase ref = new Firebase("YOUR-URL-HERE/PATH/TO/YOUR/STUFF");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
   @Override
   public void onDataChange(DataSnapshot dataSnapshot) {
       String value = (String) dataSnapshot.getValue();
       // do your stuff here with value
   }
   @Override
   public void onCancelled(FirebaseError firebaseError) {
   }
});

Upvotes: 3

Related Questions