Anthony
Anthony

Reputation: 4049

How deal with Firebase offline mode and data push?

When using Firebase, it is recommanded to use the push() command to manage data list. That's really fine, it provides a unique and ordered id for data pushed on the list.

However, when Firebase goes offline (goOffline or NetworkOffline), if the app try to push a data on a list, the completion listener is not triggered, until the app goes back online : so there is no unique id until the line is on again.

1/ Is that the expected/normal behavior ?

2/ I didn"t see in the document (as far I remember), that the push command is working differently (or only in onlinemode) in offline state. Did I miss a line somewhere ?

3/ My use case deal with data that own relationship. It means I want create an object (kind of master) in a list, and then reuse this master object id (provided by the completion listener) to build the relation between the master object and all other relevants objects. How may I deal with this offline state ?

Code Example :

    findViewById(R.id.button3).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new Firebase(getString(R.string.firebase_url)).child("stack").push().setValue(counter++, new Firebase.CompletionListener() {
                @Override
                public void onComplete(FirebaseError firebaseError, Firebase firebase) {
                    ((TextView) findViewById(R.id.textView3)).setText(String.valueOf(counter) + " - " + firebase.toString());
                }

            });
        }
    });

Edit

Here is 4 ways to add data:

Push with Listener

   findViewById(R.id.button3).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new Firebase(getString(R.string.firebase_url)).child("stack").push().setValue(counter++, new Firebase.CompletionListener() {
                @Override
                public void onComplete(FirebaseError firebaseError, Firebase firebase) {
                    ((TextView) findViewById(R.id.textView3)).setText(String.valueOf(counter) + " - " + firebase.toString());
                }

            });
        }
    });

SetValue with Listener

    findViewById(R.id.button4).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new Firebase(getString(R.string.firebase_url)).child("stackManual").child(UUID.randomUUID().toString()).setValue(counter++, new Firebase.CompletionListener() {
                @Override
                public void onComplete(FirebaseError firebaseError, Firebase firebase) {
                    ((TextView) findViewById(R.id.textView4)).setText(String.valueOf(counter) + " - " + firebase.toString());
                }

            });
        }
    });

SetValue without Listener

    findViewById(R.id.button5).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Firebase temp = new Firebase(getString(R.string.firebase_url)).child("stackManual").child(UUID.randomUUID().toString());
            temp.setValue(counter++);


            ((TextView) findViewById(R.id.textView5)).setText(String.valueOf(counter) + " - " + temp.getKey().toString());
        }

    });

Push without Listener

    findViewById(R.id.button6).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Firebase temp = new Firebase(getString(R.string.firebase_url)).child("stack").push();
            temp.setValue(counter++);

            ((TextView) findViewById(R.id.textView6)).setText(String.valueOf(counter) + " - " + temp.getKey().toString());
        }

    });

IT APPEAR THAT THE LISTENERS ARE TRIGGERED ONLY WHEN THE DATA ARE IN THE DATABASE ON THE SERVER, AND NOT ON THE LOCAL DB ! - IS THAT CORRECT ? - IS THAT DOCUMENTED ?

Because knowing that, a straight forward asynchronous application is more difficult to build now : encapsulation of asynchronous job may not be performed if offline, may it ?

Upvotes: 10

Views: 3371

Answers (2)

Anthony
Anthony

Reputation: 4049

Following what @Andrew-lee and @Frank-fan-puffelen (thank to both agree on that please), the solution appear to be :

  1. Create a unique id with push() command
  2. Add a listener (Single Value) to a firebase ref of the previous created node in (1.)
  3. Schedule your job inside the listener (here update the UI, but could be another command)

    findViewById(R.id.button7).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Firebase temp = new Firebase(getString(R.string.firebase_url)).child("stack").push();
            temp.addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    ((TextView) findViewById(R.id.textView7)).setText(String.valueOf(counter) + " - " + dataSnapshot.getKey().toString());
                }
    
                @Override
                public void onCancelled(FirebaseError firebaseError) {
    
                }
            });
            temp.setValue(counter++);
    
    
        }
    
    });
    

Upvotes: 3

Frank van Puffelen
Frank van Puffelen

Reputation: 600141

so there is no unique id until the line is on again

This last statement is not true. Firebase push ids are generated client-side and are statistically guaranteed to be unique.

You can easily check this by splitting your operation:

Firebase ref = new Firebase(getString(R.string.firebase_url));
Firebase newStackRef = ref.child("stack").push();
System.out.println("New key/push id: "+newStackRef.key());
newStackRef.setValue(counter++, new Firebase.CompletionListener() {

The logging output will show you the new push id, even when you're not connected to the network.

IT APPEAR THAT THE LISTENERS ARE TRIGGERED ONLY WHEN THE DATA ARE IN THE DATABASE ON THE SERVER, AND NOT ON THE LOCAL DB! IS THAT CORRECT ?

The completion listeners will only trigger once the data has been committed on the server.

From the guide on saving data:

If you'd like to know when your data has been committed, you can add a completion listener. Both setValue() and updateChildren() take an optional completion listener that is called when the write has been committed to the database.

However, regular event listeners (like those created with addValueEventListener()) will be triggered both for local and remote edits. These are the listeners you should use to render your UI, etc.

Upvotes: 10

Related Questions