Firebase - Access data without Callback - android

I want to get the data stored in the DB without being restricted to access it only when there is a data change.
I've seen this post from 2016:
How to access Firebase data without using addValueEventListener
Which suggested to use addValueEventListener.
I've also seen this post:
Accessing data in Firebase databse
Without good answer.
ValueEventListener will trigger the onDataChange only when the database will have a change.
How else can I access the database without something being changed in the database?
For now I will write simple harmless change in order to access the data, but i'm wondering if it's the only way to do it.
Thanks

Of course this is absolutely not true. You can retrieve data whenever you like to.
Firstly I would like to advice you to read this documentation reference.
Secondly I provide you with what you really asked for.
If you read the documentation you will notice that it states the following:
The onDataChange() method in this class is triggered once when the listener is attached and again every time the data changes, including the children.
That means that with this code:
databaseReference.removeEventListener(eventListener);
With that method you would be able to detatch any listener so it only listens once or detatch it whenever you want to.
There is a method for only retrieving data once though.
databaseReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d(TAG, "Data retrieved.");
}
...
}
This method will exactly call onDataChange once or respectively onCancelled.

Related

firebase realtime database reads data that doesnt exist

we came across what might be a major bug in the firebase database, pls see code below. the code below tries to set a value to a child "EXAMPLE" which doesnt have a read or write permission. the write operation doesn't write anything to the database and throws an error " setValue at /EXAMPLE/VALUE failed: DatabaseError: Permission denied" in the log, which is a good thing.
however a major issue is with the code that comes after which tries to read the value of child "EXAMPLE", the code actually goes into the ondatachange method and reads the value as "ONE" instead of going into the onCancelled method to throw a permission error, the data doesnt even exist in the database and there is no read or write permission for the child "EXAMPLE" so how can fireabase claim to read a value that is not even there.
myReftwo.child("EXAMPLE").child("VALUE").setValue("ONE");
myReftwo.child("EXAMPLE").child("VALUE").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot)
{
Log.d("print", dataSnapshot.getValue().toString() );
}
#Override
public void onCancelled(DatabaseError databaseError)
{
Log.d("print", databaseError.getMessage() );
}
});
In the snippet as you share it, most likely the listener is getting the value from the local cache before that cache has been updated because of the rejection from the server.
When you add a listener, Firebase tries to give you the value it expects the node to have immediately. And since you call addListenerForSingleValueEvent, it them immediately stops listening for the value. So you end up seeing only stale value from the local cache, and never see the actual value (or lack thereof) from the server.
For this reason you should not use both disk persistence and addListenerForSingleValueEvent in your app. Using addValueEventListener in the same scenario would lead to two calls to onDataChange: the first one with the value from the local cache, and the second one with the correct snapshot from the server.
For a longer answer on how these work, and why they don't result in the behavior you'd like, see: Firebase Offline Capabilities and addListenerForSingleValueEvent

Android Firebase firing onChildChanged on write [duplicate]

Currently, the Google's version of ServerValue.TIMESTAMP returns {".sv":"timestamp"} which is used as a directive for Firebase to fill that field with the server timestamp once you save the data to the Firebase server.
When you create your data on the client side however, you don't have the actual timestamp to play with yet (ie. use as the creation date). You only will have an access to the timestamp after the initial save and consequent retrieval, which - I imagine - is sometimes too late and not very elegant.
Before Google:
Update: Ignore this section as it is incorrect - I misunderstood the examples. ServerValue.TIMESTAMP always returned the {".sv":"timestamp"}.
As far as I understand in pre-google Firebase there seemed to be a server-generated timestamp available that allowed you to acquire the actual timestamp:
import com.firebase.client.ServerValue;
ServerValue.TIMESTAMP // eg. 1466094046
(ref 1, ref 2)
Questions:
Is such save/retrieval the only way to get the server-generated creation date on my model instances?
If yes can you propose a method of implementing such pattern?
Am I understanding correctly ServerValue.TIMESTAMP has changed with Google's acquisition of Firebase? Update: No, #FrankvanPuffelen replied that nothing's changed during acquisition.
Note:
I'm not considering using new Date() on client side as I've been reading it's not safe, though please share your thoughts if you think different.
When you use the ServerValue.TIMESTAMP constant in a write operation, you're saying that the Firebase Database server should determine the correct timestamp when it executes the write operation.
Let's say we run this code:
ref.addValueEventListener(new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println(dataSnapshot.getValue());
}
public void onCancelled(DatabaseError databaseError) { }
});
ref.setValue(ServerValue.TIMESTAMP);
This will execute as follows:
you attach a listener
you write a value with ServerValue.TIMESTAMP
the Firebase client immediate fires a value event with an approximation of the timestamp it will write on the server
your code prints that value
the write operation gets sent to the Firebase servers
the Firebase servers determine the actual timestamp and write the value to the database (assuming no security rules fail)
the Firebase server send the actual timestamp back to the client
the Firebase client raises a value event for the actual value
your code prints that value
If you're using ChildEventListener instead of a ValueEventListener, then the client will call onChildAdded in step 3 and onChildChanged in step 8.
Nothing changed in the way we generate the ServerValue.TIMESTAMP since Firebase joined Google. Code that worked before, will continue to work. That also means that the first answer you linked is a valid way to handle it.
I'm doing it a bit differently.
Solution 1: push() method in POJO
As I don't want to clutter my POJOs with strange getters or properties, I'm just defining a push() method inside my POJOs which looks like this:
/**
* Pushes a new instance to the DB.
*
* #param parentNode `DatabaseReference` to the parent node this object shall be attached to
*/
fun push(parentNode: DatabaseReference) {
parentNode
.push()
.apply {
setValue(this#Pojo)
child(Pojo.CREATED_AT_KEY).setValue(ServerValue.TIMESTAMP)
}
}
Then I can simply create an instance of the POJO and call push() on it which properly populates the creation time property.
This definitely makes the POJO a little less plain and involves logic a POJO shouldn't know about. However using #Exclude annotations and/or casts as outlined in some of the responses here also requires knowledge of the storing mechanism.
Solution 2: Helper or DatabaseReference extension (Kotlin)
To overcome this you can of course also just create a pushTask(task: Task) method in a helper or - if using Kotlin - an extension method to e.g. DatabaseReference which could look like this:
fun DatabaseReference.push(pojo: Pojo) {
push()
.apply {
setValue(pojo)
child(Pojo.CREATED_AT_KEY).setValue(ServerValue.TIMESTAMP)
}
}
Looking at it now I come to think that I actually like the second approach more (if I have Kotlin at my disposal - I don't like helpers). But this is probably just a matter of taste. ;)

How to use the Firebase server timestamp to generate date created?

Currently, the Google's version of ServerValue.TIMESTAMP returns {".sv":"timestamp"} which is used as a directive for Firebase to fill that field with the server timestamp once you save the data to the Firebase server.
When you create your data on the client side however, you don't have the actual timestamp to play with yet (ie. use as the creation date). You only will have an access to the timestamp after the initial save and consequent retrieval, which - I imagine - is sometimes too late and not very elegant.
Before Google:
Update: Ignore this section as it is incorrect - I misunderstood the examples. ServerValue.TIMESTAMP always returned the {".sv":"timestamp"}.
As far as I understand in pre-google Firebase there seemed to be a server-generated timestamp available that allowed you to acquire the actual timestamp:
import com.firebase.client.ServerValue;
ServerValue.TIMESTAMP // eg. 1466094046
(ref 1, ref 2)
Questions:
Is such save/retrieval the only way to get the server-generated creation date on my model instances?
If yes can you propose a method of implementing such pattern?
Am I understanding correctly ServerValue.TIMESTAMP has changed with Google's acquisition of Firebase? Update: No, #FrankvanPuffelen replied that nothing's changed during acquisition.
Note:
I'm not considering using new Date() on client side as I've been reading it's not safe, though please share your thoughts if you think different.
When you use the ServerValue.TIMESTAMP constant in a write operation, you're saying that the Firebase Database server should determine the correct timestamp when it executes the write operation.
Let's say we run this code:
ref.addValueEventListener(new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println(dataSnapshot.getValue());
}
public void onCancelled(DatabaseError databaseError) { }
});
ref.setValue(ServerValue.TIMESTAMP);
This will execute as follows:
you attach a listener
you write a value with ServerValue.TIMESTAMP
the Firebase client immediate fires a value event with an approximation of the timestamp it will write on the server
your code prints that value
the write operation gets sent to the Firebase servers
the Firebase servers determine the actual timestamp and write the value to the database (assuming no security rules fail)
the Firebase server send the actual timestamp back to the client
the Firebase client raises a value event for the actual value
your code prints that value
If you're using ChildEventListener instead of a ValueEventListener, then the client will call onChildAdded in step 3 and onChildChanged in step 8.
Nothing changed in the way we generate the ServerValue.TIMESTAMP since Firebase joined Google. Code that worked before, will continue to work. That also means that the first answer you linked is a valid way to handle it.
I'm doing it a bit differently.
Solution 1: push() method in POJO
As I don't want to clutter my POJOs with strange getters or properties, I'm just defining a push() method inside my POJOs which looks like this:
/**
* Pushes a new instance to the DB.
*
* #param parentNode `DatabaseReference` to the parent node this object shall be attached to
*/
fun push(parentNode: DatabaseReference) {
parentNode
.push()
.apply {
setValue(this#Pojo)
child(Pojo.CREATED_AT_KEY).setValue(ServerValue.TIMESTAMP)
}
}
Then I can simply create an instance of the POJO and call push() on it which properly populates the creation time property.
This definitely makes the POJO a little less plain and involves logic a POJO shouldn't know about. However using #Exclude annotations and/or casts as outlined in some of the responses here also requires knowledge of the storing mechanism.
Solution 2: Helper or DatabaseReference extension (Kotlin)
To overcome this you can of course also just create a pushTask(task: Task) method in a helper or - if using Kotlin - an extension method to e.g. DatabaseReference which could look like this:
fun DatabaseReference.push(pojo: Pojo) {
push()
.apply {
setValue(pojo)
child(Pojo.CREATED_AT_KEY).setValue(ServerValue.TIMESTAMP)
}
}
Looking at it now I come to think that I actually like the second approach more (if I have Kotlin at my disposal - I don't like helpers). But this is probably just a matter of taste. ;)

Firebase onDataChanged fire twice when using ServerValue.TIMESTAMP (Android) [duplicate]

Currently, the Google's version of ServerValue.TIMESTAMP returns {".sv":"timestamp"} which is used as a directive for Firebase to fill that field with the server timestamp once you save the data to the Firebase server.
When you create your data on the client side however, you don't have the actual timestamp to play with yet (ie. use as the creation date). You only will have an access to the timestamp after the initial save and consequent retrieval, which - I imagine - is sometimes too late and not very elegant.
Before Google:
Update: Ignore this section as it is incorrect - I misunderstood the examples. ServerValue.TIMESTAMP always returned the {".sv":"timestamp"}.
As far as I understand in pre-google Firebase there seemed to be a server-generated timestamp available that allowed you to acquire the actual timestamp:
import com.firebase.client.ServerValue;
ServerValue.TIMESTAMP // eg. 1466094046
(ref 1, ref 2)
Questions:
Is such save/retrieval the only way to get the server-generated creation date on my model instances?
If yes can you propose a method of implementing such pattern?
Am I understanding correctly ServerValue.TIMESTAMP has changed with Google's acquisition of Firebase? Update: No, #FrankvanPuffelen replied that nothing's changed during acquisition.
Note:
I'm not considering using new Date() on client side as I've been reading it's not safe, though please share your thoughts if you think different.
When you use the ServerValue.TIMESTAMP constant in a write operation, you're saying that the Firebase Database server should determine the correct timestamp when it executes the write operation.
Let's say we run this code:
ref.addValueEventListener(new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println(dataSnapshot.getValue());
}
public void onCancelled(DatabaseError databaseError) { }
});
ref.setValue(ServerValue.TIMESTAMP);
This will execute as follows:
you attach a listener
you write a value with ServerValue.TIMESTAMP
the Firebase client immediate fires a value event with an approximation of the timestamp it will write on the server
your code prints that value
the write operation gets sent to the Firebase servers
the Firebase servers determine the actual timestamp and write the value to the database (assuming no security rules fail)
the Firebase server send the actual timestamp back to the client
the Firebase client raises a value event for the actual value
your code prints that value
If you're using ChildEventListener instead of a ValueEventListener, then the client will call onChildAdded in step 3 and onChildChanged in step 8.
Nothing changed in the way we generate the ServerValue.TIMESTAMP since Firebase joined Google. Code that worked before, will continue to work. That also means that the first answer you linked is a valid way to handle it.
I'm doing it a bit differently.
Solution 1: push() method in POJO
As I don't want to clutter my POJOs with strange getters or properties, I'm just defining a push() method inside my POJOs which looks like this:
/**
* Pushes a new instance to the DB.
*
* #param parentNode `DatabaseReference` to the parent node this object shall be attached to
*/
fun push(parentNode: DatabaseReference) {
parentNode
.push()
.apply {
setValue(this#Pojo)
child(Pojo.CREATED_AT_KEY).setValue(ServerValue.TIMESTAMP)
}
}
Then I can simply create an instance of the POJO and call push() on it which properly populates the creation time property.
This definitely makes the POJO a little less plain and involves logic a POJO shouldn't know about. However using #Exclude annotations and/or casts as outlined in some of the responses here also requires knowledge of the storing mechanism.
Solution 2: Helper or DatabaseReference extension (Kotlin)
To overcome this you can of course also just create a pushTask(task: Task) method in a helper or - if using Kotlin - an extension method to e.g. DatabaseReference which could look like this:
fun DatabaseReference.push(pojo: Pojo) {
push()
.apply {
setValue(pojo)
child(Pojo.CREATED_AT_KEY).setValue(ServerValue.TIMESTAMP)
}
}
Looking at it now I come to think that I actually like the second approach more (if I have Kotlin at my disposal - I don't like helpers). But this is probably just a matter of taste. ;)

Firebase on Android - how to make event handler for data retrieval fire when I need it to?

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.

Categories

Resources