I'm wondering on Android, how the underlying actual mechanism work when you add an listener to the database. Is it just more frequent pulling or something else special?
Update:
To make it clearer, I understand what a listener is, but I meant how does the 'listening' scheme work, how a client (Android) knows the data on the server changed. Is it just a periodical pulling? (and Firebase engineers already do the hard work to cover that and make it easy for us).
Looks like firebase is not open-source.
// Attach an listener to read the data at our posts reference
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
System.out.println(snapshot.getValue());
}
#Override
public void onCancelled(FirebaseError firebaseError) {
System.out.println("The read failed: " + firebaseError.getMessage());
}
});
disclaimer: this is a simplified description of how things work at the time of writing. Things may have changed by the time your read it.
When your app connects to the Firebase Database, it opens a web socket connection from the device to a Firebase server. This connection stays open for the lifetime of your app or until you call goOffline().
When you attach a listener, the client sends the location (and potential query parameters) to the server. The server adds that listener to a list of all listeners of all connected clients. It then also sends back the initial data for that listeners.
Whenever a write operation is committed to the database, the server scans the listeners. For each relevant listener, the server sends an update to the client over the open web socket.
It happens Asynchronously
Adding listeners to a node reference will fetch any changes made to the node reference asynchronously
void onDataChange(DataSnapshot snapshot)
This method will be called with a snapshot of the data at this location. It will also be called each time that data changes.
void onCancelled(FirebaseError error)
This method will be triggered in the event that this listener either failed at the server, or is removed as a result of the security and Firebase rules. For more information on securing your data, see: Security Quickstart
Example
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
System.out.println(snapshot.getValue());
Users users = snapshot.getValue(Users.class) //This is your POJO class
String name = users.getName(); //Other getter methods to fetch data from firbase
}
#Override
public void onCancelled(FirebaseError firebaseError) {
System.out.println("The read failed: " + firebaseError.getMessage());
}
});
It's explained in the guide Firebase - Retrieve Data on Android
This method is triggered once when the listener is attached and again every time the data, including children, changes.
Related
Consider the following example:
I'm writing a value on a test/ node, and I have set up a rule, that will allow the write, only if the "(new value) equals (old value + 1)", that is newData.val() == data.val() + 1
Let's say that the initial value of the test/ node is 0.
If the client goes offline and executes the following commands:
testRef.setValue(1);
testRef.setValue(2);
testRef.setValue(3);
testRef.setValue(4);
testRef.setValue(5);
Then when he goes back online, the value 5 will be written in the database, but I'm not sure I understand why, since 5 != 0 + 1. I guess that this happens due to the caching of the previous values in the local database, but unfortunately that's not the result I am trying to achieve. I want the server to reject that value since it doesn't follow the rules of the database.
Is there a way to achieve that?
Or is there any other workaround so that I can implement the following:
testRef.setValue(1); -> write value to local database -> check if value follows the rules of the online database -> if the value does not follow the rules or if we can't check that because we are offline, then delete the value from the local database
I assume that for your question "offline" means the client has no connection. In my testing, I simulated that by enabling Airplane Mode.
Firebase offline capabilities are described in the user guide. One detail provided there that is important to your question is:
The Firebase Realtime Database client automatically keeps a queue of
all write operations that are performed while your app is offline ...
When the app regains connectivity, all of the operations are sent to
the Firebase Realtime Database server.
You can see this behavior using the code below, which adds a completion listener to the setValue() calls. In my test, I put the device into Airplane Mode (offline), ran the code, and then disabled Airplane Mode to go back online. A log message is generated for each setValue(), confirming that the write operations were queued and sent when a connection was re-established. This explains why the writes satisfy your validation rule: the client does not send one write request with the final value 5, it sends five requests with the original incrementing values.
You can confirm that the rule works by running the test again without first resetting the value of test back to 0. Each write will fail.
This code also demonstrates how the Firebase client handles changes made when the client is offline, and which are later rejected by security rules. While offline, the change is made in the client cache and the onDataChange() callback fires with the new (unvalidated) value. Later, when the client goes online and the change is rejected by the server, onDataChange() fires again with the previous value.
final DatabaseReference ref = FirebaseDatabase.getInstance().getReference("test");
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d(TAG, "onDataChange: test=" + dataSnapshot.getValue(Integer.class));
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
});
final DatabaseReference.CompletionListener completionListener =
new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
if (databaseError == null) {
Log.d(TAG, "setValue() Success");
} else {
Log.d(TAG, "setValue() Failed " + databaseError.getMessage());
}
}
};
ref.setValue(1, completionListener);
ref.setValue(2, completionListener);
ref.setValue(3, completionListener);
ref.setValue(4, completionListener);
ref.setValue(5, completionListener);
I want to get value of child. But I have to wait what data changed. But I don't want to get value without datachange. (without listener)
I use below method :
FirebaseDatabase.child("benim-degerim").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
snapshot.getValue().toString()
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
I want to snapshot.getValue() without listener. How can I do it ?
Loading data over the internet takes time. That's why it's done asynchronously, so that the user of your app can continue using the app while the data is being downloaded.
Since there is no way to make the internet instant, downloads will always be asynchronous and thus require a listener (or for other frameworks, some other form of callback).
The fastest way I've found to get used to asynchronous methods is to reframe your problem from "first get data, then do something with it" to "when we get the data, do something with it". This typically means that you move the code that does "something" into the onDataChange() method.
I am new to Firebase and need some help with a query to retrieve data from a table. I am currently able to access and retrieve the data that I need from firebase, however, the timing is the problem I am having an issue with.
From everything I've seen, the firebase database requires me to add event listeners to the Query or DatabaseReference objects. I am trying to download the contents of a node called "questions" before a method to display the question contents is called, however, I cannot control the timing of the firing of the event which downloads the data, and as a result my display method is always called before the firebase event fires.
How can I execute a query when I want, and be sure it will be completed before a certain section of my code executes? I am used to traditional RDBs where you execute a query and get its results and then move forward with your logic. The need to use an event handler with firebase is what I am having a hard time with. I have even tried moving the definition of the firebase reference object and the event handler into onCreate() and moved the code that calls my display method into onStart() without any success - same problem. The data I am trying to get does not change so I only need to download it once at the beginning to have available for the display method.
Here is an image of my "questions" node which is a child of the root.
image of the child "questions" node on my firebase DB
Here is my code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Get Firebase DB reference
firebase = FirebaseDatabase.getInstance();
fdbRef = firebase.getReference("questions");
// [START Question_event_listener]
fdbRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get Questions object and use the values to update the UI
objQuestions = dataSnapshot.getValue();
Log.w("Firebase:", "In Firebase ValueEventListener");
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Questions failed, log a message
Log.w("Firebase Error:", "onCancelled:", databaseError.toException());
Toast.makeText(ReviewActivity.this, "Failed to load question!", Toast.LENGTH_SHORT).show();
}
});
//. . . remaining onCreate logic removed for simplicity
} //end of onCreate
#Override
public void onStart() {
super.onStart();
// I moved this logic from onCreate to onStart but did not help...
// Firebase retrieve must execute before I call any of these
if (list_type == MainActivity.LIST_UNREVIEWED_DOCS)
displayNewReviewForm();
else if (list_type == MainActivity.LIST_REVIEWS)
displayCompletedReview();
else // (list_type == MainActivity.LIST_DRAFTS)
displayDraftReview();
}
Other alternatives if I can't get this resolved may be to move this retrieve logic to the prior Activity in my sequence and pass the retrieved data as an extra to this activity - but that seems really silly to have to do such a thing. I would think I should be able to get data from a DB when I need it... not when it feels like giving it to me.
I appreciate any help getting me past this issue.
Your code is downloading the snapshot data containing all the data at the first go only, and with Firebase, you cannot download data timely, you can only do it through different references.
What I would suggest you to do is, to have a DatabaseReference of q01, q02 respectively and then call data as in when required.
If your Keys "q01", "q02" are static, which they are looking at the scenario. I would suggest you to have their DatabaseReferences:
question_one = firebase.getReference("q01");
question_two = firebase.getReference("q02");
question_three = firebase.getReference("q03");
//Once you have the reference, you can call their ValueListeners respectively
question_one.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get Questions object and use the values to update the UI
objQuestions = dataSnapshot.getValue();
Log.w("Firebase:", "In Firebase ValueEventListener");
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Questions failed, log a message
Log.w("Firebase Error:", "onCancelled:", databaseError.toException());
Toast.makeText(ReviewActivity.this, "Failed to load question!", Toast.LENGTH_SHORT).show();
}
});
After looking at this a bit more, I came up with 2 possible solutions to the problem I had.
The first one I sort of mentioned already in my original question post, however it's not ideal in my opinion. It basically involves relocating the firebase retrieve logic to the prior Android Activity and passing the retrieved data to the Activity I need it in as an Extra. In my case the data is a HashMap so I would need to use the serialize versions of the methods to pass the serialized content to the desired Activity.
The best solution, is much simpler. I basically relocated the logic that I had in the onStart() function (which is calling my custom display methods) and moved it inside of the Firebase Event Listener's onDataChange() method, right after the call to dataSnapshot.getValue(). This ensures that I get the data before I call my display methods. This seems to be working well now.
I am working on Firebase to retreive data using addValueEventListener from Android SDK but i found sometime the response time take minimum 1 minute to get the result.
My Code :
Firebase firebase = new Firebase("https://example.firebaseio.com/");
firebase.child("XYZ").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d("DataFirebase","onDataChange : "+dataSnapshot);
Toast.makeText(getApplicationContext(),"onDataChange",Toast.LENGTH_SHORT).show();
}
#Override
public void onCancelled(FirebaseError firebaseError) {
Log.d("DataFirebase","onCancelled : "+firebaseError);
Toast.makeText(getApplicationContext(),"onCancelled",Toast.LENGTH_SHORT).show();
}
});
This is my above code , please let me know , how can i get the result instantly from a key. Please suggest me some solution.
I too have a problem of slowness with firebase realtime db. Try calling, FirebaseDatabase.getInstance().setPersistenceEnabled(true); inside your Application class's "onCreate" method.
Be aware that this will save data on your device and the second call and above will be much faster but from cache and can be outdated, Read this for handling syncing data from server when needed immediately.
I'm using Firebase for Android for the chat component of our app.
I'm having trouble figuring out how to reliably implement status updates on each chat message.
For example, showing "Sending.." when the chat is being synced with the server, and having a success feedback after sync.
I have a onChildAdded listener that supplies the messages to my adapter. However, this listener is fired immediately when each node is added locally, and I can't check the status of each node
My Current solution is to keep a set of node keys, and add keys whenever I push something to Firebase. Then on the setValue callback, I remove the node key from the set. However, this is very unreliable since the nodes can be synced when the calling activity has been destroyed, etc.
I am wondering if there is a simpler way to check if each node has been synced to the server?
Thanks!
From the Firebase documentation on writing 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.
With this handy code sample:
ref.setValue("I'm writing data", new Firebase.CompletionListener() {
#Override
public void onComplete(FirebaseError firebaseError, Firebase firebase) {
if (firebaseError != null) {
System.out.println("Data could not be saved. " + firebaseError.getMessage());
} else {
System.out.println("Data saved successfully.");
}
}
});