Firebase onDataChange skips over first fire then fires normally afterwards - android

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.

Related

Firebase onDataChange method increase value

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.

Set Limit to Zero in Parse Counter

I'm trying to set my counter in parse to not go below zero when the score is being decremented, at the moment it can go to negative numbers. How can I set the minimum limit to be zero?
This is what I've managed to do so far:
btnPointTeamD.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
object.increment("team_d_score");
object.saveInBackground();
}
});
btnMinusTeamC.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
object.increment("team_c_score", -1);
object.saveInBackground();
}
});
Cloud code has what's called beforeSave and afterSave triggers. beforeSave is what you need here.
A beforeSave trigger contains all of the new data (note: none of the old) and you can check object.dirty("key"); to see if that field has changed. You also don't have to do any checks for this specific case.
Parse.Cloud.beforeSave("ClassName", function(request, response) {
var object = request.params.object;
if( object.get("team_c_score") < 0 ) object.set("team_c_score", 0);
response.success();
});
Some notes: If you return response.error(), the save will note go through, so this is how you validate input. A field contains illegal characters, or data you didn't expect? Throw an error so it doesn't get saved.
You also shouldn't put anything in the success response. That will cause an error.
This function gets called automatically if it exists, and will always be called. You can't skip it. Even if you update data from the dashboard, this gets called. Same with afterSave triggers, although modifying an object in those will not do anything unless you save it.
This should go in your main.js, or a file required by main.js. I have a folder for each of my custom classes. Each class has a classNameController.js, which contains the beforeSave, afterSave, initializer, and any cloud code functions relating directly to that object.
Main requires each of these controllers, which opens up all of the Parse.Cloud endpoints to the server.
beforeSave and afterSave triggers on hosted Parse.com had a 3 second timeout. I am not aware if there is a timeout for parse-server. I've never tested it. But don't have more than a couple server calls to be safe.

Android Firebase get value of child without DataChange

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.

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.

Firebase sometimes not calling database

I'm trying to set a listener single value event and it works usually. (Doing this with a pull to refresh - it works, even if I kill the app and reopen, or have the android system reclaim the memory in the background).
However after probably a certain period of time (couple hours), I go back to the app and try to access it and the code below doesn't fire anymore. And it stays in this inoperable state (however I verify that everything is there). It lasts until I basically log out and log back in.
I put a log in the onDataChange method and it doesn't fire. I put it outside of it and it fires.
Here is my code:
FirebaseDatabase.getInstance()
.getReference()
.child(context.getString(R.string.firebase_jobs))
.child(userId).
addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot jobSnapShot : dataSnapshot.getChildren()) {
JobItem jobItem = jobSnapShot.getValue(JobItem.class);
// Add the ID into the jobItem
jobItem.setId(dataSnapshot.getKey());
// Set the jobItem
arrayList.add(jobItem);
subscriber.onNext(jobItem);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(TAG, "onCancelled: " + databaseError.getMessage());
}
});
}
Has anybody experienced the code of where it sometimes is not able to call in the code? Thanks!
Edit: I try to log out while its firing and I receive the log error message:
E/FirebaseRetrieveData: onCancelled: Permission denied
So the thing is firing, it is just taking a long time to get the data back... if it ever does!
That is the intended behavior of addListenerForSingleValueEvent(). As described from docs:
It is triggered one time and then will not be triggered again.
Unless you're aiming for something different? Did I misunderstand something in your post? Were you aiming for you are only to listen to a specific single value? If yes, I think you should've used addValueEventListener() instead.
EDIT:
Just in case others see this. Here is the latest docs link for addListenerForSingleValueEvent().

Categories

Resources