android firebase get string from datasnapshot to the outside - android

suppose i am getting data from firebase as
DatabaseReference mdb=FirebaseDatabase.getInstance().getReference("users/"+id);
mdb.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String name=dataSnapshot.child("name").getValue();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
//point b
its working perfectly....
if i try to print that string name using
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String name=dataSnapshot.child("name").getValue();
System.out.println(name);
}
it will print that string.
but what if i want that string name outside the listener, like in first code where i have added a comment //point b
i tried defining string name globally.
but it only prints the println which is inside onDatachange()... but it will print null at //point b...even if i defined the String name globally.
like
String name;
DatabaseReference mdb=FirebaseDatabase.getInstance().getReference("users/"+id);
mdb.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
name=dataSnapshot.child("name").getValue();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
//point b
edit
actually its a function returning a string value like
public String getData(String id)
{
String name;
DatabaseReference mdb=FirebaseDatabase.getInstance().getReference("users/"+id);
mdb.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
name=dataSnapshot.child("name").getValue();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return name;
}

Data is loaded from Firebase asynchronously, which unfortunately means that the approach you're trying won't be possible
The easiest way to see this is by simplifying your code to just some log statements:
DatabaseReference mdb=FirebaseDatabase.getInstance().getReference("users/"+id);
System.out.println("Before attaching listener");
mdb.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println("Inside onDataChange");
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // Don't ignore errors
}
});
System.out.println("After attaching listener");
When you run this code it prints:
Before attaching listener
After attaching listener
Inside onDataChange
That is probably not the output you expected, but explains perfectly why you're not able to return the name from your function: by the time your return statement is executed, the data hasn't been loaded from Firebase yet.
There is no way to wait for the data to come available. Even if there was, it'd lead to your application being blocked and Android displaying the infamous "Application Not Responding" dialog.
Instead you will need to move the code that needs access to the name into the onDataChange method as you've done when printing it.
Also see:
Setting Singleton property value in Firebase Listener
can't get values out of ondatachange method
Firebase/Android: Adding retrieved values from Firebase to arraylist returns null pointer exception

You can't access the variable outside the onDataChange. So if you want to access the variable maybe for function/method you can implement the method inside the onDataChange. As the onDataChange will be the one running last. So make sure anything relating to the variable being done inside the onDataChange.

Related

How to display data automatically without having to use a button?

Currently, to get the data from the firebase database, I use button to get the data and display them into textview. However, every time i uploaded data, i need to click the button in order to get the data to be displayed.
Therefore, is there a way to display the data right away after the upload session instead of clicking the button to display them?
Below code shows how i display the data at the moment by clicking button which works successfully.
Thanks!
fetch=(Button) v.findViewById(R.id.bFetch_Schedule);
fetch.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
dayRef.child("Subject").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String value = dataSnapshot.getValue(String.class);
textViewSubject_Schedule.setText(value);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
dayRef.child("What is in our class?").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String value = dataSnapshot.getValue(String.class);
textViewWhat_Schedule.setText(value);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
});
You need to use addValueEventListener() instead of addListenerForSingleValueEvent().
The listener you provided to addValueEventListener() is called each time the data changes.
The listener you provided to addListenerForSingleValueEvent() is called once with the value of the data at the location.
You may also want to check addChildEventListener(). All information available at the related doc.
You are using addListenerForSingleValueEvent, which gets the value from Firebase and then stops listening. If you use the same code with addValueEventListener it will keep receiving updates.
dayRef.child("Subject").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String value = dataSnapshot.getValue(String.class);
textViewSubject_Schedule.setText(value);
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
You'd typically put this code in a lifecycle event of the activity, such as onCreate or onStart.

Firebase in android not removing nodes

I have this item in the database
And I have a method for removing items, but is not working, this is the method
public void deleteBag(String bagUid, final FirebaseDeleteBaglListener listener) {
Query query = dbReference.child(FirebaseChild.bags.name()).child(bagUid);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
dataSnapshot.getRef().setValue(null);
listener.notifyBagDeleted();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
It gets the reference well, but the method setValue(null) is doing nothing (I've also tried the removeValues().
I don't get any exception or some kind of response, I hope you can help me.
Thanks!
I beleive you have a model class that inserts your items into the database.
so what you have to do is to use that class to reference the object directly and perform the required action on it. Something like this.
public void deleteBag(String bagUid, final FirebaseDeleteBaglListener listener) {
Query query = dbReference.child(FirebaseChild.bags.name()).child(bagUid);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//This shouldn't be there
//dataSnapshot.getRef().setValue(null);
//instead use the modelclass you used inserting data, to reference the object
Modelclass mode = dataSnapshot.getValue(Modelclass.class);
/** Note this line indicates the getter and setter method for the particular object that is being referenced**/
mode.setItem(null)
listener.notifyBagDeleted();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
When you execute a query against the Firebase Database, there will potentially be multiple results. You're using a ValueEventListener, so the snapshot contains a list of all results. Even if there is only a single result, the snapshot will contain a list of one result.
You need to loop over DataSnapshot.getChildren() to get the individual items matching your query:
public void deleteBag(String bagUid, final FirebaseDeleteBaglListener listener) {
Query query = dbReference.child(FirebaseChild.bags.name()).child(bagUid);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child: dataSnapshot.getChildren()) {
child.getRef().removeValue();
}
listener.notifyBagDeleted();
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
}

Getting the timestamp wrong firebase android

Hey I am trying to get the current TIMESTAMP in the app when clicking a button but the TIMESTAMP is incorrect. The TIMESTAMP sometimes shows the time ahead and sometimes 10 minutes before time. Here is the code
timestapmReference.addValueEventListener(new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
final long timeStampLong = (long) dataSnapshot.child("time").child("timestampQuestionSeen").getValue();
final DatabaseReference questionSeenReference = FirebaseDatabase.getInstance().getReference().child("users").child(uid).child("questions").
child(imagename);
questionSeenReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (!dataSnapshot.hasChild("questionSeen")) {
questionSeenReference.child("questionSeenTime").setValue(timeStampLong);
questionSeenReference.child("questionSeen").setValue("1");
}
questionSeenReference.removeEventListener(this);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
timestapmReference.removeEventListener(this);
}
public void onCancelled(DatabaseError databaseError) {
}
});
timestapmReference.child("time").child("timestampQuestionSeen").setValue(ServerValue.TIMESTAMP);
Your problem is that you are setting those values insider the onDataChange method. There is no need to do such a thing. Move this 2 lines:
questionSeenReference.child("questionSeenTime").setValue(timeStampLong);
questionSeenReference.child("questionSeen").setValue("1");
outside that method and remove that listener because is useless. To set a value you only need to use the setValue() method directly on the reference.
Hope it helps.

When I nest two value event listeners, do they both run asynchronously or the the lower one waits for the higher one to be completed?

I am working on an android application, using Firebase and Android Studio.
I had issues with retrieving values with value event listeners because i couldnt set values inside the onDataChange method to be used else where because it runs asynchronously, so the listener may or may not have completed its purpose before the value is needed outside. So I solved this issue by putting whatever needs to be done inside the onDataChange method(real life saver!).
But
the value i am retrieving in the onDataChange also happens to be needed inside another listener. like this
DatabaseReference mLastnameReference = rootRef.child("users").child("12345678").child(phone);
ValueEventListener lastnameListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
try {
yourid = dataSnapshot.getValue().toString();
}
catch (NullPointerException e){
//handle user not registered here
}
DatabaseReference mBioReference = FirebaseDatabase.getInstance().getReference().child("users").child(yourid).child("status");
ValueEventListener bioListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Status = dataSnapshot.getValue().toString();
mTextField = (TextView) findViewById(R.id.status);
mTextField.setText(Status);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
mBioReference.addValueEventListener(bioListener);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
mLastnameReference.addValueEventListener(lastnameListener);
the value im waiting for in the first listener is yourid . I also need that value to get a database reference to the node for the nested listener to be executed. But when i ran this code, both of them appear to be running at the same time. Hence, getting a null exception on the nested database reference, because the yourid appears to not have been retrieved at that time.
In the code you shared, the value of users/12345678/$phone will be read before you attach the nested listener. But the value might be null, which you code doesn't handle correctly:
DatabaseReference mLastnameReference = rootRef.child("users").child("12345678").child(phone);
ValueEventListener lastnameListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String id = String.valueOf(dataSnapshot.getValue());
if (id != null) {
DatabaseReference mBioReference = FirebaseDatabase.getInstance().getReference().child("users").child(id).child("status");
ValueEventListener bioListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Status = dataSnapshot.getValue(String.class);
mTextField = (TextView) findViewById(R.id.status);
mTextField.setText(Status);
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
};
mBioReference.addValueEventListener(bioListener);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
};
mLastnameReference.addValueEventListener(lastnameListener);
I've replaced the try-catch with a regular if and made id a local variable, which removes the chance that some other part of your code sets it to null. It also throw exceptions in onCancelled now, since it's best to not ignore those authorization problems.

Knowing when Firebase has completed API call?

I am having some trouble knowing when my Firebase API call is finished. After reading the Firebase documentation I have found the following:
Value events are always triggered last and are guaranteed to contain updates from any other events which occurred before that snapshot was taken.
I understand this to mean that only after all the onChildAdded call is finished, then the ValueEventListener is called. As a result, I thought that I can populate my RecyclerView in the onChildAdded function and then the onSingleValueListener call, I can simply finish animating my loading screen (which has started animating before this function call) and proceed. However, I have run into an issue where I put some careful System.out.println statements and found that in my case, Test 1 is called before Test 2 is ever called. This causes problems because this is actually the opposite behavior of what I wanted: I wanted the onChildAdded function to finish and then call the onSingleValueListener function that prints out Test 1 to be called. Is there any reason why this is happening? Any way around this? I would appreciate an explanation on why this is happening. Thanks!
public void getComments(final String postId, final Activity activity, final View fragmentView, final View progressOverlay) {
final Firebase commentsRef = firebaseRef.child("/comments");
Firebase linkRef = firebaseRef.child("/posts/" + postId);
linkRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println("Test 1");
if (progressOverlay.getVisibility() == View.VISIBLE) {
progressOverlay.setVisibility(View.GONE);
AndroidUtils.animateView(progressOverlay, View.GONE, 0, 200);
fragmentView.findViewById(R.id.rv_view_comments).setVisibility(View.VISIBLE);
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
linkRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
commentsRef.child(dataSnapshot.getKey()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Comment comment = dataSnapshot.getValue(Comment.class);
System.out.println("Test 2");
application.getCommentsRecyclerViewAdapter().getCommentsList().add(comment);
application.getCommentsRecyclerViewAdapter().notifyDataSetChanged();
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
You may want to use the **FirebaseRecyclerAdapter** class that the Firebase team makes available in FirebaseUI-Android (see https://github.com/firebase/FirebaseUI-Android/blob/master/database/src/main/java/com/firebase/ui/database/FirebaseRecyclerAdapter.java)
In your gradle file add the line below (check here for latest version number in the readme)
compile 'com.firebaseui:firebase-ui-database:0.4.3'
With this code "Firebase linkRef = firebaseRef.child("/posts/" + postId);" I could see that you're using legacy Firebase API. Its deprecated now!
Kindly update your code to new Firebase 3.x.x API.
Below two are independent async call; Based on your use-case, you can use either one of the listener to read your data.
1. linkRef.addListenerForSingleValueEvent(new ValueEventListener() {});
2. linkRef.addChildEventListener(new ChildEventListener() {});
You can refer the firebase document to get more information about database listeners.
https://firebase.google.com/docs/database/android/retrieve-data
With the following code snippet, you can retrieve and populate your list of comments.
public void getComments(final String postId, final Activity activity, final View fragmentView, final View progressOverlay) {
DatabaseReference commentsRef = firebaseRef.child("/comments");
DatabaseReference linkRef = commentsRef.child("/posts/" + postId);
linkRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Iterate through data-snapshot, and update your Adapter dataset
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Comment comment = snapshot.getValue(Comment.class);
application.getCommentsRecyclerViewAdapter().getCommentsList().add(comment);
}
application.getCommentsRecyclerViewAdapter().notifyDataSetChanged();
// Dismiss your loading progressbar
if (progressOverlay.getVisibility() == View.VISIBLE) {
progressOverlay.setVisibility(View.GONE);
AndroidUtils.animateView(progressOverlay, View.GONE, 0, 200);
fragmentView.findViewById(R.id.rv_view_comments).setVisibility(View.VISIBLE);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Handle fail case here
}
});
Hope this would help you!

Categories

Resources