Firebase Datasnapshot returns null value - java

I want to retrieve this value from this node ("id"), and the value i get is null. I have googled so many solutions that this might have to do with asynchronous way or something, i guess?
This is the database, and the highlighted node is the value i would like to get:
This is my code:
reference = FirebaseDatabase.getInstance().getReference();
id = null;
Query lastQuery = reference.child("Donation Request").orderByKey().limitToLast(1);
lastQuery.addListenerForSingleValueEvent(new ValueEventListener()
{
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot)
{
if (dataSnapshot.child("id").exists())
{
id = dataSnapshot.child("id").getValue().toString();
int index = Integer.parseInt(id) + 1;
id = Integer.toString(index);
Toast.makeText(getApplicationContext(), "It works!!!", Toast.LENGTH_SHORT).show();
}
else
{
id = "1";
Toast.makeText(getApplicationContext(), "It doesn't work.", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError)
{
}
});
Most appreciated if someone can help me out of this!

When you execute a query against the Firebase Database, there will potentially be multiple results. So the snapshot contains a list of those results. Even if there is only a single result, the snapshot will contain a list of one result.
Your onDataChange needs to handle this list by looping over dataSnapshot.getChildren()):
reference = FirebaseDatabase.getInstance().getReference();
id = null;
Query lastQuery = reference.child("Donation Request").orderByKey().limitToLast(1);
lastQuery.addListenerForSingleValueEvent(new ValueEventListener()
{
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot)
{
for (DataSnapshot snapshot: dataSnapshot.getChildren()) {
if (snapshot.hasChild("id"))
{
id = snapshot.child("id").getValue(String.class);
int index = Integer.parseInt(id) + 1;
id = Integer.toString(index);
Toast.makeText(getApplicationContext(), "It works!!!", Toast.LENGTH_SHORT).show();
}
else
{
id = "1";
Toast.makeText(getApplicationContext(), "It doesn't work.", Toast.LENGTH_SHORT).show();
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError)
{
throw databaseError.toException(); // never ignore errors.
}
});
A few more notes:
Any use of id needs to happen inside onDataChange, or be called from there. Outside of that, you won't have any guarantees that id will have been assigned the value you expect.
Using toasts for debugging is bound to become confusing. I highly recommend using Log.d(...) and friends, and studying the output (and its order) in the logcat output of your app.

Related

"println needs a message" error in android

I want to read Music_ID of the group with Playlist_ID of 2 in firebase.
The following error occurs.
java.lang.NullPointerException: println needs a message
This is my firebase realtime database.
And this is my code.
database = FirebaseDatabase.getInstance();
storage = FirebaseStorage.getInstance();
dref = FirebaseDatabase.getInstance().getReference();
private void Startplaylist(String mood) {
DatabaseReference plist = dref.child("Playlist");
plist.orderByChild("Playlist_ID").equalTo(2).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
// Log.i("Value", dataSnapshot.getValue().toString());
String music_id = dataSnapshot.child("Music_ID").getValue(String.class);
Log.i("Value_id", music_id);
str_musictitle.setText(music_id);
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
An alarm pops up that an error occurs in this part.
Log.i("Value_id", music_id);
I think "music_id" is not being read.
I tried to change part
String music_id = dataSnapshot.child("Music_ID").getValue(String.class);
to String music_ids = dataSnapshot.child("Music_ID").getValue().toString(); and run it, but I couldn't get the desired result.
When you execute a query against the Firebase Database, there will potentially be multiple results. So the snapshot contains a list of those results. Even if there is only a single result, the snapshot will contain a list of one result.
The code in your onDataChange will need to handle this list by looping over dataSnapshot.getChildren(). Something like this:
DatabaseReference plist = dref.child("Playlist");
plist.orderByChild("Playlist_ID").equalTo(2).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot: dataSnapshot.getChildren()) { // 👈 Loop over results
String music_id = snapshot.child("Music_ID").getValue(String.class); // 👈 Get value for this result
Log.i("Value_id", music_id);
str_musictitle.setText(music_id);
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
throw error.toException(); // 👈 Never ignore possible errors
}
});

addListenerForSingleValueEvent returns dataSnapshot value null when I add query to it

I am working on a android project. There's a bug related to Firebase Database. So, I need an experienced Android developer which is much familiar whith the firebase Databse.
Bug: I am using addListenerForSingleValueEvent and when I try to get all data, It returns data and when I set query for status = ACTIVE, It returns dataSnaphot null even though the database has an ACTIVE entry. When I use addValueEventListener , it works fine.
Please help me.
private void getMeetingData(){
//Constants.MEETING_STATUS = meeting_status
//Constants.MEETING_STATUS_ACTIVE = ACTIVE
//Constants.MEETING_STATUS_INACTIVE = INACTIVE
databaseReference0 = FirebaseDatabase.getInstance().getReference().child("meeting"); //is this correct
databaseReference0.orderByChild(Constants.MEETING_STATUS).equalTo(Constants.MEETING_STATUS_ACTIVE).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
//MeetingModel getAllData = null;//wait, should i run it in debug mode
//Log.i("printMeeting",getAllData.toString());
for (DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()) {
MeetingModel getAllData = dataSnapshot1.getValue(MeetingModel.class);
arrayList.add(getAllData);
Log.d("printMeeting",getAllData.toString());
}
if (arrayList.isEmpty()) {
} else {
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
Mutating global variables is not advised because most Firebase APIs are asynchronous and they may not yet be initialized when you try to use them. As an example, this pseudo-code wouldn't work.
// initialize...
getMeetingData()
// do something with arrayList now...
When fetching data at a location, the DataSnapshot that is returned will contain the data you requested at the top level. When performing a query at a location, the DataSnapshot that is returned will contain the data you requested as its children.
// A Realtime Database FETCH
FirebaseDatabase.getInstance()
.getReference()
.child("meeting")
.child(meetingId)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
if (!snapshot.exists()) {
// TODO: Handle not-exists
Log.i(TAG, "Requested meeting doesn\'t exist");
return;
}
MeetingModel requestedModel = snapshot.getValue(MeetingModel.class);
// TODO: do something with `requestedModel`
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
// don't ignore this event, at least log it
Log.e(TAG, "Failed to fetch data: " + databaseError.getMessage());
}
});
// A Realtime Database QUERY
FirebaseDatabase.getInstance()
.getReference()
.child("meeting")
.orderByChild(Constants.MEETING_STATUS) // <-- becomes a query here
.equalTo(Constants.MEETING_STATUS_ACTIVE)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot querySnapshot) {
if (!querySnapshot.hasChildren()) {
// TODO: Handle no matching data
Log.i(TAG, "Requested query returned no results");
return;
}
ArrayList<MeetingModel> matchingMeetings = new ArrayList<>();
for (DataSnapshot meetingSnapshot : querySnapshot.getChildren()) {
matchingMeetings.add(meetingSnapshot.getValue(MeetingModel.class));
}
// or, the simpler form:
// GenericTypeIndicator<List<MeetingModel>> t = new GenericTypeIndicator<List<MeetingModel>>() {};
// List<MeetingModel> matchingMeetings = snapshot.getValue(t);
// TODO: do something with `matchingMeetings`
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
// don't ignore this event, at least log it
Log.e(TAG, "Failed to fetch data: " + databaseError.getMessage());
}
});
You could also use the Task API:
private Task<List<MeetingModel>> getActiveMeetings() {
return FirebaseDatabase.getInstance()
.getReference()
.child("meeting")
.orderByChild(Constants.MEETING_STATUS) // <-- becomes a query here
.equalTo(Constants.MEETING_STATUS_ACTIVE)
.get() // <- fetch data from server
.onSuccessTask(querySnapshot -> { // <- mutate the returned data
if (!querySnapshot.hasChildren()) {
throw new DataNotFoundException();
}
GenericTypeIndicator<List<MeetingModel>> t = new GenericTypeIndicator<List<MeetingModel>>() {};
return snapshot.getValue(t);
});
}
Then when you need to use it:
getActiveMeetings()
.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
List<MeetingModel> meetings = task.getResult();
// TODO: Handle
} else {
Exception ex = task.getException();
// TODO: Handle
}
});

Reading from a Firebase Database

I'm trying to read from a Firebase Database, I've read and looked everywhere, but I'm at a dead end.
Here's all that I've done.
Dependencies:
implementation 'com.google.firebase:firebase-storage:9.2.1'
implementation 'com.google.firebase:firebase-database:9.2.1'
implementation 'com.google.firebase:firebase-auth:9.2.1'
implementation 'com.google.firebase:firebase-core:9.2.1'
minSdkVersion: 15
DatabaseReference mDatabase;
mDatabase = FirebaseDatabase.getInstance().getReference();
then in a Button onClick method, I put the listener:
mDatabase.child("List").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String savedData = dataSnapshot.getValue(String.class);
Log.d(TAG, "snapshot: " + savedData);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d(TAG, "Error");
}
});
Here is a look at the Database.
Would appreciate the input.
You're trying to read a String value under List. But right under List there's no string value, rather there's a list of objects under there. To get the actual values, you'll need to navigate the JSON structure in your code.
Something like:
DatabaseReference mDatabase;
mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.child("List").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot tierSnapshot: dataSnapshot.getChildren()) {
Log.d(TAG, tierSnapshot.getKey(); // "Tier 1", "Tier 1 B"
for (DataSnapshot secondSnapshot: tierSnapshot.getChildren()) {
Log.d(TAG, secondSnapshot.getKey(); // "Tier 2", "Tier 2 B"
String str = secondSnapshot.getValue(String.class);
Log.d(TAG, str); // null, "2 B"
Long num = secondSnapshot.getValue(long.class);
Log.d(TAG, num); // 2, null
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.w(TAG, "Error", databaseError);
}
});
An extra thing to note is that your values are of a different type, with the first one being the number 2, while the second one is a string "2 B". Hence the two getValue() calls, to get the specific type out of there. You could also just do secondSnapshot.getValue() and deal with the resulting object as you'd usually do (e.g. call toString() on it).

How to check for a specific value exist in the firebase realtime database?

I am trying to write a query to check if there is any "Pending" status on mcustDeliveryStatus. I want to check in Order node that is there any Pending status for mcustDeliveryStatus.
I wrote the following query
mDatabase = FirebaseDatabase.getInstance().getReference("Order");
final Query query = mDatabase.OrderByChild("mcustDeliveryStatus").equalTo("Pending");
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
Toast.makeText(VWelcome.this, "new order", Toast.LENGTH_LONG).show();
Intent i = new Intent(VWelcome.this, ViewOrderRequest.class);
startActivity(i);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
This query works fine but the problem is intent runs even when there is no Pending status in my Order node. Anyone knows why?
Since you annotate your DataSnapshot object with #NonNull, it means that you tell the compiler that your object cannot be null, so it will always return something. With other words, if there are no results, it will return an empty DataSnapshot object. To solve this, you should check how many children are inside that object using the following lines of code:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference orderRef = rootRef.child("Order");
Query query = orderRef.orderByChild("mcustDeliveryStatus").equalTo("Pending");
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
long count = dataSnapshot.getChildrenCount();
if (count > 0) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String mGasBand = ds.child("mGasBand").getValue(String.class);
Log.d(TAG, mGasBand);
}
} else {
Log.d(TAG, "No data!");
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage()); //Don't ignore errors!
}
};
query.addListenerForSingleValueEvent(valueEventListener);

Firebase reading issue. Object always null

I have a method which should initialize my field variable currentTask. I don't understand why I can't read my object from firebase. Here is the method:
private void getCurrentTask() {
final DatabaseReference dRef1 = database.getReference().child("Users").child(uid).child("CurrentTask");
dRef1.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
currentTask = dataSnapshot.getValue(CurrentTask.class);
} else {
Toast toast = Toast.makeText(TasksListActivity.this, "no magic", Toast.LENGTH_LONG);
toast.show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
if (currentTask == null) {
Toast toast = Toast.makeText(this, "magic)", Toast.LENGTH_LONG);
toast.show();
}
}
Toast no magic never appears, so the object exists. But, in the end of the method toast magic appears, which means currentTask == null. Even after initialization!
And here is my database:
#Roasario and #ReazMurshed 's answer is both right. But let me make it more simple by describing how your code doesn't work as you expected:
private void getCurrentTask() {
...
dRef1.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
... (point 1)
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
... (point 2)
}
Firebase Database load your data asyncronously. It means (to make it simple) that the process of getting data does not interfere with your main process. With that in mind, the code in (point 2) does not always get executed after code in (point 1), and vice versa. Though usually code in (point 1) get executed after (point 2), it is not always like that. So you should consider that the code you write in (point 1) can be executed at anytime.
Then you should write your code with that concept. Meaning that if you want to do anything to a variable inside (point 1) (like filling currentTask with dataSnapshot value then check if it is null), you should place it all inside (point 1)
Hope this help.
Note that onDataChange is asynchronous, so your if statement will always return false because you're checking if it is null while the data hasn't been read before. You should check if it is null inside of onDataChange (to assure the data has been read) like this:
private void getCurrentTask() {
final DatabaseReference dRef1 = database.getReference().child("Users").child(uid).child("CurrentTask");
dRef1.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
currentTask = dataSnapshot.getValue(CurrentTask.class);
if (currentTask == null) {
Toast toast = Toast.makeText(this, "magic)", Toast.LENGTH_LONG);
toast.show();
}
} else {
Toast toast = Toast.makeText(TasksListActivity.this, "no magic", Toast.LENGTH_LONG);
toast.show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
}
Modify your reference url a bit like this.
final DatabaseReference dRef1 = database.getReference().child("Users").child(uid);
Now create a class to represent each of your nodes.
public class User {
public UserCharacter Character;
public UserCurrentTask CurrentTask;
public String Email;
public UserTasks Tasks;
}
Now inside your onDataChange you need to do this.
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
User mUser = dataSnapshot.getValue(User.class);
currentTask = mUser.CurrentTask;
} else {
// Show toast or something.
}
}
And as #Roasario stated, the onDataChanged function is Async. So you can't get the actual value while you check for null value.

Categories