Android - Persist data retrieved from Firebase - android

I have an activity and a model called CourseDetails.
String getData;
DatabaseReference mRef = FirebaseDatabase.getInstance().getReference().child("courses").child("Business");
mRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
CourseDetails c = dataSnapshot.getValue(CourseDetails.class);
getData = c.getCourseName();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
textview1.setText(getData);
Using above code throws NullPointerException at last line above. But if I put textview1.setText(getData) into the ValueEventListener, under getData = c.getCourseName(), the data can be displayed correctly.
Methods I found working are using SharedPreferences or setting data from a method such as public void display(String data) { textview1.setText(data); }. But what are the other ways to keep the retrieved data even if the data is outside ValueEventListener?
For instance I want to persist the data added into an ArrayList.
ArrayList<String> listData;
DatabaseReference mRef = FirebaseDatabase.getInstance().getReference().child("courses").child("Business");
mRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
CourseDetails c = dataSnapshot.getValue(CourseDetails.class);
String code = c.getCourseCode();
String name = c.getCourseName();
String CodeName = code + " " + name;
listData.add(CodeName);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
// data in ArrayList should be able to display here
StringBuilder builder = new StringBuilder();
for (String s : listData) {
builder.append(s + "\n");
}
textview1.setText(builder.toString());
How to achieve this kind of persistence?

As per my understanding, Firebase will notify all it's data listener attached to specific references (database references wherever the addValueEventListener is added) when those specific data gets modified. That is when
onDataChange will be called, when there is modification of the data at those database references,
(besides modification the method will always be called first time).
And this happens
asynchronously, so in the first case where null occurs because we don't know whether data is retreived from Firebase and
as far as I know, Android's main thread cannot be put on hold or pause until we retreive the data that's why we use Asynchronous tasks in Android.
So, I think the best way to do specific updates or task on data change is within onDataChange method. So, like you stated it could be
done by making those changes within onDataChange itself or by calling some other method from onDataChange.
Or, if you are using
adapter then, notifying adapter about the change within onDataChange. Also, you can take a look at other choice i.e. FirebaseRecyclerAdapter then,
it will handle the update automatically without any extra effort.

Related

firebase realtime storage getting a specific value from all children

i am building a app using firebase that requires an admin page and in it i want to make a list of all the usernames of users registered in the system and i am using this code:
Usernames = new ArrayList<>();
usersdb = FirebaseDatabase.getInstance().getReference().child("Users");
usersdb.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot user:dataSnapshot.getChildren()) {
Usernames.add(user.child("username").getValue().toString());
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
adp = new ArrayAdapter<String>(context,android.R.layout.simple_list_item_1,Usernames);
lv.setAdapter(adp);
and for some reason nothing shows up in the list view in the end anyone knows why?
and my data structure looks like this:(the random letters are user uids)
Users
----sdfbsif
-----------email
-----------username
-----------password
----djgsvnv
-----------email
-----------username
-----------password
You're calling setAdapter() before the data is available from your listener. addValueEventListener is asynchronous and returns immediately, which means you're passing an empty list to the adapter. Put log messages throughout your code to see the order in which it's executing.
Instead, you could call setAdapter in the callback after all the data from the snapshot is available.

How to get only newly added data from the Firebase?

As i am creating the chat app, for it i am using the Firebase.
It works perfectly, only one problem which i am getting that each time i am getting whole list of data from Firebase, when new item is added or deleted into it.
What i want that , only newly added record should come from Firebase , not whole list of data.
Please check my below code for it
ArrayList<ChatMessage> MY_ARRAYLIST= new ArrayList<>();
MsgViewHolder viewModel = ViewModelProviders.of(this).get(MsgViewHolder.class);
LiveData<DataSnapshot> liveData = viewModel.getDataSnapshotLiveData();
liveData.observe(this, dataSnapshot -> {
if (dataSnapshot != null) {
MY_ARRAYLIST.clear(); // I NEED TO CLEAR THE ARRAY-LIST TO GET THE REFRESHED DATA..I DO NOT WANT TO LOAD WHOLE LIST ON EACH TIME
if (dataSnapshot.exists())
for (DataSnapshot ds : dataSnapshot.getChildren()) {
ChatMessage bean = ds.getValue(ChatMessage.class);
assert bean != null;
MY_ARRAYLIST.add(bean);
}
}
});
As in above code i am using MY_ARRAYLIST.clear() to clear data, to get the newly added record.On each time from DataSnapshot , i am getting the whole list of data, whenever new record added or deleted.Is their any method to get only newly added data NOT whole List from Firebase?
You can use addChildEventListener() to you DatabaseReference which will notify you on different callback method when any child is add/removed/updated/deleted or moved
databaseRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
// If any child is added to the database reference
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
// If any child is updated/changed to the database reference
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
// If any child is removed to the database reference
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
// If any child is moved to the database reference
}
#Override
public void onCancelled(FirebaseError firebaseError) {
Log.e("FirebaseListAdapter", "Listen was cancelled, no more updates will occur");
}
});
The DataSnapshot in each callback will provide you the information of the child
This is by design, in a real-time system there is no concept of the "latest" data because it's always changing. However, if you want to only display items added to the list after the page has loaded, you can do the following:
var newItems = false;
var eventsList = new Firebase('https://*****-messages.firebaseio.com/');
eventsList.on('child_added', function(message) {
if (!newItems) return;
var message = message.val();
$.notification(message.message);
});
eventsList.once('value', function(messages) {
newItems = true;
});
above answer original by (anant)
(my) another solution
if you have control over your database schema you can add a 'datetime' element in your object and store the value of the time just before adding it to database in Epoch format, then you can simply get the list of objects in newly added order with limit like this.
ds.orderBy("datetimeSent", Direction.DESCENDING).limit(10);
I think you can use this listener onChildAdded() I provided a link to the documentation.
Listen for child events
You can try Query for query on database,add on child as date and time and query based on that data filed after every result update the query to last sync date and time

Filter geoFire queries by certain criteria before populating viewHolder in Android

I have successfully implemented geoFire to get the keys of entries from a main firebase node within the user's area.
I have then attempted to store those keys in a new, unique node, in order to use them later to populate a viewHolder, as this seems to be best practice.
The problem is that I don't want all of the objects stored in the unique node to actually populate the viewHolder; I only want a subset of them.
If I filter inside the populateViewHolder method, e.g.,
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
SwarmReport retrievedModel = dataSnapshot.getValue(SwarmReport.class);
if(!retrievedModel.isClaimed()){
viewHolder.bindSwarmReport(retrievedModel);
}
}
I get some items in the viewHolder that are blank.
If, instead, I try to filter before setting up the FirebaseAdapter, e.g. in my addGeoQueryEventListener methods, I have to add an addListenerForSingleValueEvent to check the criteria by which I aim to filter:
public void onKeyEntered(String key, GeoLocation location) {
claimCheckKey = key;
final DatabaseReference claimCheckRef = FirebaseDatabase.getInstance().getReference("all").child(key);
claimCheckRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
SwarmReport claimCheckSwarmReport = dataSnapshot.getValue(SwarmReport.class);
if (!claimCheckSwarmReport.isClaimed()){
swarmReportIds.add(claimCheckKey);
}
}
It seems that onGeoQueryReady executes before addListenerForSingleValueEvent in onKeyEntered is finished. Any recommendations on how to get onGeoQueryReady to wait for all of the swarmReportIds.add(claimCheckKey)s to complete?
In case it's helpful, here's a snapshot of my firebase structure:
I don't really understand what you are trying to achieve but I will take a guess and say that you want not only the closest key but you also want to filter the closest key based on some other criteria, e.g., closest restaurant then prices,
The workaround I did to achieve this, (still looking for better options) is to set a flag inside the onDatachange method that I called on inside the onKeyentered method, that compares entries inside that node, (for sake of argument we will say prices)
UPDATE
#Override
public void onKeyEntered(final String key, GeoLocation location) {
final DatabaseReference db = FirebaseDatabase.getInstance().getReference("<your ref>"+key);
db.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
String dishPrice=ds.getValue(String.class);
int price=Integer.parseInt(dishPrice);
if (price<=12) {
isMatch=true;
}
}
if(isMatch){
getRestaurants(key);
}
isMatch=false;
}
And outside the for each loop of onDataChange I set to execute the function that populates the arraylist of my adapter only when the function is true
getRestaurants method
private void getRestaurants(final String id) {
final DatabaseReference temp=FirebaseDatabase.getInstance().getRef("<your
ref>"+id)
temp.child(id).addListenerForSingleValueEvent(new
ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Restaurant restaurant = dataSnapshot.getValue(Restaurant.class);
restaurant.setName(restaurant.getName());
restaurant.setImageUri(restaurant.getImageUri());
restaurant.setUid(id);
//add elemens to arraylist
resList.add(restaurant);
adapter.notifyDataSetChanged();
}
Another suggestion would be to make a function on the cloud, (assuming again) in your case that all you need is to monitor whether the value of "claimed" changes, so you can set an onUpdateevent trigger or and onWrite and check the value in onChanged() and get the current value (true/false)after that you can make use of geoFire queries and get the close by users and update their nodes depending on the value.
Event Triggers
Cloud Functions

Retrieve String out of addValueEventListener Firebase

I want to receive a string from addValueEventListener() method I use to resell the data from the database Firebase. The data arrive correctly.
But when certain to get the string out of that method to use it in another, it returns nothing.
You have tips?
I already tried putExtras and also create a method on purpose but it did not work.
final DatabaseReference mPostReference = FirebaseDatabase.getInstance().getReference().child("user-daily").child(getUid()).child("2017-Year");
mPostReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final ArrayList<String> labels = new ArrayList<String>();
for (DataSnapshot data : dataSnapshot.getChildren()){
final DailyItem dailyItem = data.getValue(DailyItem.class);
labels.add(dailyItem.mese);
}
title.setText(labels.get(position));
a = title.getText().toString();
}
#Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(view.getContext(),"database error",
Toast.LENGTH_SHORT).show();
}
});
//this return null... why?
String title = a;
The data is loaded from Firebase asynchronously. By the time you run title = a, the onDataChange method hasn't been called yet. Set some breakpoints in a debugger to verify this, it's key to understanding how asynchronous loading works.
The solution is to reframe your problem from "first get the object, then do blabla with the title" to "start getting the object; once the object is available, do blabla with the title".
In code this translates to:
final DatabaseReference mPostReference = FirebaseDatabase.getInstance().getReference().child("user-daily").child(getUid()).child("2017-Year");
mPostReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final ArrayList<String> labels = new ArrayList<String>();
for (DataSnapshot data : dataSnapshot.getChildren()){
final DailyItem dailyItem = data.getValue(DailyItem.class);
labels.add(dailyItem.mese);
}
title.setText(labels.get(position));
// Do blabla with the title
String title = title.getText().toString();
}
#Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(view.getContext(),"database error",
Toast.LENGTH_SHORT).show();
}
});
Many developers new to Firebase (and other modern web APIs, as they all work this way) struggle with this problem. So I recommend you also check out their questions and answers:
Cannot access firebaseObjectObservable outside of set
Android Firebase get value of child without DataChange
Value of a global variable is reset after it is initialised in ValueEventListener
can't get values out of ondatachange method
ArrayList not updating inside onChildAdded function
Setting Singleton property value in Firebase Listener
and most others in this list of search results
In order to retrieve the string from method addValueEventListener in viewmodel or any other network call, it is recommended to use the either MutableLiveData<T> or LiveData<T> and observe the same in your activity. Observer will observe the changes, and as soon as string got filled up, the observer method will automatically give you string which you are looking.
You need to create reference variable for the LiveData<T> reference_variable wherever your addValueEventLister is located and set its value in your addValueEventListener.
And then in your viewmodel create the returning value function like below...
Observe this function in your activity and you will have your string.
public MutableLiveData<TotalRunsWicketsAndData> getDisplayableDetails() {
return observableLiveData;
}
I am using MutableLiveData here.
This is a trick which does it. It would be easy to do so if you have less data to retrieve from ValueEventListener.
Inside the onDataChange(), use a setText to set the required value in it. Keep the visibility of this text view as "Gone". Then retrieve using getText outside the ValueEventListener.
You can retrieve the whole list by using GenericTypeIndicator. Follow the official guide on here

Saving data from Firebase into a list

I'm currently developing an Android app that deals with storing book translations. I'm using Firebase to hold all the data online, and I have no trouble pulling data from it and storing them as Book objects. I have a master list of books in the main class that stores all the books from the Firebase database, however the list doesn't seem to update properly.
public static List<Book> books = new ArrayList<Book>();
protected void onCreate(Bundle savedInstanceState) {
myFirebaseRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot bookSnapshhot : snapshot.getChildren()) {
Book book = bookSnapshhot.getValue(Book.class);
books.add(book);
System.out.println(book.getAuthor() + " - " + book.getTitle());
for (Chapter chapter : book.getChapters()) {
System.out.println(chapter.getTitle());
}
for (Genre genre : book.getGenres()) {
System.out.println(genre.getGenre());
}
System.out.println(books.size());
}
}
#Override public void onCancelled(FirebaseError error) { }
});
System.out.println(books.size());
}
In that body, the size of books is correct (right now it is only 1). However if I call the same print statement outside of that code body (the last line), it gives me 0. I don't know why, because it successfully adds it inside the onDataChange() method but it seems to not apply the changes on the outside.
I think that your problem is about the asyncronously of the ValueEventListener.
The onDataChange is called asynconously.
You have to use the books after the onDataChange is called.
The addValueEventListener() will fetch the data from Firebase asynchronously.
What your code depicts is that you are adding ValueEventListener and then printing System.out.println(books.size());. This means your logs gets displayed after you add ValueEventListener(which doesn't means your listener gets invoked). At this point your listener is not invoked yet and so isn't onDataChange(). Thus you won't ever get updated count outside the listener scope.

Categories

Resources