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.
Related
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
I'm having an issue with 2 separate methods, essentially the same issue where the database reference is firing and retrieving all the correct paths from the relevant nodes, but skips over the first fire on onDataChange then fires as expected afterwards giving the values needed.
The general context of these 2 methods is retrieving the value at the database reference using a code/value (specified path) to get to its relevant value. This value is retrieved and used elsewhere in the program.
I've looked at many problems regarding onDataChange not firing. Solved many of those issues elsewhere in my program but somehow these 2 methods are persisting with this issue. Ive run debug multiple times and dont understand how its showing and getting the correct paths but skips the first run on onDataChange where other methods implementing the exact same principle is running perfecting.
Im only posting the first issue
in onCreate
databaseReference_AUTH_TABLE = FirebaseDatabase.getInstance().getReference(AUTH_TABLE_TAG); verified = false;
Context is im using a dialog to authenticate a code. Check if the code exists in the database. And if so have the rest of the program run what i need it to do
public void authenticateProductID(final String code){
databaseReference_AUTH_TABLE.child(code).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if(dataSnapshot.exists() && !verified){//Exists and is not verified yet
PID = dataSnapshot.getValue().toString();
verified = true;
return;
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
public void showPopupProduct_btn(View view){
final Dialog dialogProductVerification = new Dialog(this);
dialogProductVerification.setContentView(R.layout.layout_popup_product);
Button authenticate = dialogProductVerification.findViewById(R.id.btnPopupProductVerification);
authenticate.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EditText verificationCode = dialogProductVerification.findViewById(R.id.editTextPopupCode);
code = verificationCode.getText().toString();
if(noDuplicateCode(code)){
authenticateProductID(code);
if(verified){
getPackage(PID, code);
txtResult.setText(code);
}
else{
Toast.makeText(POSActivity.this, "Authentication Failed", Toast.LENGTH_SHORT).show();
}
}
}
});
dialogProductVerification.show();
}
Because onDataChange isn't fired the first time, verified is false. But 2nd button click everything is perfect.
firbase node
Basically my app will be finished when this is resolved. Any help will be much appreciated. Thank you in advance
Firebase APIs are asynchronous, which means that the onDataChange() method returns immediately after it's invoked and the callback from the Task it returns, will be called some time later. There are no guarantees about how long it will take. So it may take from a few hundred milliseconds to a few seconds before that data is available. Because that method returns immediately, the value of your verified boolean that you are trying to use, is not populated from the callback yet. So simply creating it as a global variable won't help you at all.
Basically, you're trying to use a value synchronously from an API that's asynchronous. That's not a good idea. You should handle the APIs asynchronously as intended.
A quick solve for this problem would be to move the code that queries the second node inside the first callback (inside the onDataChange() method) so-called nested queries, otherwise, I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.
I'm trying to get a value and then increase that value by one. The problem is I cannot get this value out of onDataChange method, if I do the job inside the method I get loop and it continuously adding ones to this value. What should I do?
Here is my code:
rootRef.child("users").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
int rat = Integer.parseInt(dataSnapshot.child(current_user).child("rating").getValue().toString());
System.out.println(rat);
rat = rat + 1;
rootRef.child("users").child(current_user).child("rating").setValue(rat);
System.out.println(rat);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
If you need to receive data one time from firebase db then you need to use addListenerForSingleValueEvent(...) instead of
addValueEventListener(...). Then onDataChange() will return only one time.
addValueEventListener will call on each time when there is any value update.
Here in the above situation each time when you increment the values and update the node again addValueEventListener is called repeatedly , thus it behave like infinite loop.
Firebase APIs are asynchronous, meaning that the onDataChange() method that you are talking about returns immediately after it's invoked and the callback from the Task it returns, will be called some time later. There are no guarantees about how long it will take, it may take from a few hundred milliseconds to a few seconds before that data is available. Because that method returns immediately, the value of your rat variable you're trying to use it outside the onDataChange() method, will not have been populated from the callback yet.
Basically, you're trying to return the value of rat synchronously from an API that's asynchronous. That's not a good idea. You should handle the APIs asynchronously as intended.
A quick solve for this problem would be to use the value rat only inside the onDataChange() method, otherwise I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.
I am using firebase geofire library to fetch key's based on location but because of thousands of keys are returned in onKeyEntered() event every time I have to take the key refer firebase and get the object back and set it to listview becoming very slow. I tried commenting all other work in onKeyEntered() to see how fast geofire is then I was surprised withing 900 milli's all callbacks received.
So now what is the best optimized way to get the data from firebase using the key passed in onKeyEntered() callback and set it to listview so that even for thousands of entries listview should load fast
I thought of AsyncTask in every callback give the fetching data work to AsyncTask and proceed with next callback key and do same, but not sure thats correct.
Or load only few and then load as scroll's is also good idea but geofire returns keys from all over the database so there is no option to get only few latest one so not sure how to implement it.
This is what I am trying but list view loads very slow.
#Override
public void onKeyEntered(String key, GeoLocation location) {
Log.d("geoevent", key);
mDatabaseTemp = FirebaseDatabase.getInstance().getReference("/posts/" + key);
mDatabaseTemp.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Post post = new Post();
post = dataSnapshot.getValue(Post.class);
mPosts.add(post);
mPostAdapter.notifyDataSetChanged();
mRecycler.smoothScrollToPosition(mPosts.size() - 1);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(getActivity(), "error" + databaseError, Toast.LENGTH_LONG).show();
}
});
}
Question of the type "how to best" are notoriously difficult to answer. But I'll give a few hints about the general behavior of the system that you may not be aware of.
the Firebase Database client interacts with the network and disk on a separate thread. When data is available for your client, it calls your handlers on the main/UI thread. This means that putting your calls in an AsyncTask is only useful if you'll be doing significant work in the callback.
the Firebase Database client pipelines the calls to the server over a single connection. To read more about why that affects performance, see Speed up fetching posts for my social network app by using query instead of observing a single event repeatedly.
loading thousands of items into a mobile device over the network is in general not a good idea. You should only load data that you'll show to the user and thousands of items is well beyond what can be reasonably presented on a mobile screen. When developing a location based app, you could show a map where the user indicates a hotspot and you use Geoqueries to only request items around that spot.
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.