Firebase DataSnapshot.getValue() is null - java

I've been looking at the other errors here in SO, in regards to mine and still can't seem to get anywhere, since I keep getting the DataSnapshot.getValue() returning null...
First I get the db-ref:
private DatabaseReference f_database = FirebaseDatabase.getInstance().getReference().child("maps_data");
Then in my OnCreate method inside my activity I've added a listener:
f_database.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()){ <<<< Problem is here, value is null
for(DataSnapshot snapshot:dataSnapshot.getChildren()){
double lati = 0;
// Get UsersLocationFavorites object and use the values to update the UI
UsersLocationFavorites userLocFav = snapshot.getValue(UsersLocationFavorites.class);
LatLng location = new LatLng(userLocFav.getFavoriteSpot().getLatitude(), userLocFav.getFavoriteSpot().getLongitude());
gmap.addMarker(new MarkerOptions().position(location).title("Old Marker"));
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
And looking inside my firebase database, I have the following:
I've double checked spellings ("maps_data"), I've looked an many SO-problems here, where I can't find one that fits my problem.
Can anyone see, what I've done wrong?

You're mixing up the databases provided by Firebase.
All the code you're showing is accessing Firebase Realtime Database. But the screenshot is showing data in Firestore. These are completely diffrent database products. If you want to read data out of Firestore, you should be using the Firestore SDK instead.

Related

How to update entry in Firebase Database based on an attribute value

I am using the Firebase Realtime Database with Android in Java. I have the following database screenshot:
I would like to change the availability value (from 0 to 1) for the ingredient with the attribute "ingredient_name = Lime". The attribute ingredient_name is actually something like a primary key meaning that there will be no other database entry with this specific name.
I tried the following code
DatabaseReference rootRef;
rootRef = FirebaseDatabase.getInstance("https://....app").getReference();
String ingredientToBeUpdate = "Lime";
rootRef.child("ingredients").orderByChild("ingredient_name").equalTo(ingredientToBeUpdate).child("availability").setValue(1);
But I get the error "Cannot resolve method 'child' in 'Query'". Can you tell me how to do this update properly? So I would like to update the value from the database entries who attribute "ingredient_name" is equal to a certain string ingredientToBeUpdate.
Firebase doesn't support so-called update queries, where you send a condition and the new data to the database and it them writes the new data for all nodes matching the condition.
Instead you will need to execute the query in your application code, loop through the results, and update each of them in turn:
rootRef
.child("ingredients")
.orderByChild("ingredient_name")
.equalTo(ingredientToBeUpdate)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot ingredientSnapshot: dataSnapshot.getChildren()) {
ingredientSnapshot.getRef().child("availability").setValue(1);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
}
Also see:
Firebase Android, set value where x = x?
Is it possible to update a specific child's value without ID or key in firebase realtime database from android on button click?

How to get in order data from two different children in Firebase Realtime Database?

I have two models photos and videos. To retrieve photos I call an addChildEventListener and to get videos I call another addChildEventListener added.
Code example
databaseReference = FirebaseDatabase.getInstance().getReference("videos");
Query queryContent= databaseReference;
queryContent.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
videos v= dataSnapshot.getValue(videos.class);
objectItems.add(v);
loading = true;
contentViewPager.setAdapter(new discover_fullscreen_adapter(getApplicationContext(), objectItems));
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onChildRemoved(#NonNull DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
For photos is the same, I just change the reference, and the model
databaseReference = FirebaseDatabase.getInstance().getReference("photos");
photos p = dataSnapshot.getValue(photos.class);
objectItems.add(p)
First I add the videos and then the photos and the order is messy because I want to get videos and photos together in the order they were taken. Like a phone gallery. We have photos and videos ordered in the way they were taken (timestamp in his case). How can I achieve the same knowing that photos and videos are in different nodes and they are called by different models?
How to do it with Firebase Realtime Database
My models are based on getter and setter, I don't want to make the question bigger.
Thank you
I want to get videos and photos together in the order they were taken. Like a phone gallery.
You can perform a Firebase Realtime Database query only on a single node. You cannot get data across multiple nodes using a Query. If you want to get the "photos", as well as the "videos" in a single go, then both should exist within the same node. So you should create another node called "photosAndVideos" where you should add all the data. This practice is called denormalization and is a common practice when it comes to Firebase. For a better understanding, I recommend you see this video, Denormalization is normal with the Firebase Database.
Once you have all data under a single node, you can then perform the desired query according to a timestamp. Please see my answer from the following post:
How to save the current date/time when I add new value to Firebase Realtime Database
To see how to add a timestamp property to your object. By default Firebase orders the results ascending. However, if you need a descending order, please see my answer from the following post:
How to arrange firebase database data in ascending or descending order?
Edit:
You have to check each object from the results an instance of which class is. So when you read the data, you cannot only cast the value. You'll have to read each object and request the correct class in the call to getValue().

Is there any way to only proceed once I have obtained data from Firebase in Android?

I am working on an app for a hotel, which enables hotel management to report and view concerns and issues. I am using Android and Firebase for this app.
Here is the database structure of a reported concern:
To minimize data download and optimize speed, I am adding "Active" and "Resolved" nodes in the database, like below:
Now, the hotel wants me to add the function to create an Excel report of concerns closed/resolved within the past month. For this, I will be attaching a Single Value Event Listener on the "resolved" node, get keys of resolved concerns, then for each key, fetch data from "allConcerns" node, store each node's data into an ArrayList of String. After which I will use this JSON to Excel API for Android to create Excel file.
I am able to access keys of resolved concerns with this code:
DatabaseReference resolvedReference = FirebaseDatabase.getInstance().getReference()
.child(getApplicationContext().getResources().getString(R.string.concerns))
.child(getApplicationContext().getResources().getString(R.string.resolved));
final ArrayList<String> keys = new ArrayList<>();
resolvedReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
//Getting keys of all resolved concerns in keys arraylist here
for (DataSnapshot ds : snapshot.getChildren()){
keys.add(ds.getValue(String.class));
}
//Storing JSON data in this arraylist
final ArrayList<String> data = new ArrayList<>();
for(int i = 0; i<keys.size() ; ++i){
String key = keys.get(i);
//Getting data of each concern here
FirebaseDatabase.getInstance().getReference().child(getApplicationContext().getResources().getString(R.string.allConcerns))
.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
String type = snapshot.child("type").getValue().toString();
Log.i("Type", type);
if(type.equals("0")) {
SafetyConcernClass s = snapshot.getValue(SafetyConcernClass.class);
Log.i("Snapshot of key", s.toString());
data.add(s.toString());
}
else{
GembaWalkClass g = snapshot.getValue(GembaWalkClass.class);
Log.i("Snapshot of key", g.toString());
data.add(g.toString());
}
Proof proof = snapshot.child("proof").getValue(Proof.class);
Log.i("Proof", proof.toString());
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
//Issue I am facing is here
Log.i("Data size", String.valueOf(data.size()));
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
The real issue here is while logging data.size(). Since Firebase is asynchronous, FOR loop ends before data is fetched and entered into the data ArrayList, hence it gives me a size of 0. And since no data is fetched, I can't create an Excel file.
My question is, how can I make sure I am proceeding to log data.size() ONLY after data of respective resolved concerns is stored in the ArrayList?
The typical approach is to keep a counter or a countdown latch to track how many of the concern snapshots you've already downloaded. Once the counter reaches keys.size() you know that you're done.
Also see Setting Singleton property value in Firebase Listener
You should write your method
addListenerForSingleValueEvent
using AsyncTask or Kotlin coroutines
and in onPostExecute() of AsyncTask, you can proceed to further action.

Firebase Android read data -> processing->update data "Global variable problem"

I have to take data from Firebase and remove delete some value and then save the data to Firebase. Here is my read function in my Activity:
private void readData() {
mAuth = FirebaseAuth.getInstance();
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference().child("users");
DatabaseReference currentUserDB = mDatabase.child(mAuth.getCurrentUser().getUid());
currentUserDB.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
Map<String,Object> map = (Map)dataSnapshot.getValue();
kwotaKonta= ((String)map.get("account"));
When I make everything in "onDataChange" it's removed over and over so
I tried to create global variable named "kwotaKonta" and make it onCreate but when function is over "kwotaKonta" is null. How can I get the value from onDataChange?
Thx!
its remove over and over
This is happening because you are using addValueEventListener instead of addListenerForSingleValueEvent. The ValueEventListener is triggered every time something changes in your database. So if you remove something from the database, your onDataChange() method fires again.
To solve this, please change:
currentUserDB.addValueEventListener(/* ... */);
to
currentUserDB.addListenerForSingleValueEvent(/* ... */);
From the official documentation pelase see:
addListenerForSingleValueEvent(ValueEventListener listener) - Add a listener for a single change in the data at this location.
and
addValueEventListener(ValueEventListener listener) - Add a listener for changes in the data at this location.

Firebase always return null after the for each loop

I want to extract database from Firebase. In for each loop, it shows me the description from database but after the for each loop when I check it again for that description, it shows me null and not only for description but for all other values also. Here is the code for extracting data from Firebase. It doesn't shows any kind of error. Help me.
public void getDatafromFirebase(){
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
DataSnapshot Videoreference = dataSnapshot.child("Videos");
for (DataSnapshot videoschild : Videoreference.getChildren()){
DataSnapshot description = videoschild.child("Description");
DataSnapshot duration = videoschild.child("Duration");
DataSnapshot title = videoschild.child("Title");
DataSnapshot thumbnail = videoschild.child("Thumbnail");
details.setDescription(String.valueOf(description.getValue()));
details.setDuration(String.valueOf(duration.getValue()));
details.setTitle(String.valueOf(title.getValue()));
details.setThumbnail(String.valueOf(thumbnail.getValue()));
Log.e("details",details.description);
list.add(details);
}
Log.e("Size",list.get(0).description);
adapter.notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(getActivity(), databaseError.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
If I understand correctly, you are trying to log the value of your description property (or any other property), outside the callback and you are getting null, right?
This ia happening because Firebase APIs are asynchronous, meaning that 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 description property 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 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 use the description value 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.

Categories