Querying data from Firestore to Arraylist but got nothing? - java

I have trouble adding data in ArrayList.
I tried to add data in array list but got nothing
Here's my code
public class fmMain extends Fragment {
private ArrayList<markerList> posList = new ArrayList<markerList>();
public fmMain() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {
//Firebase get data
stores.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
for (QueryDocumentSnapshot documentSnapshot: queryDocumentSnapshots){
markerList m = new markerList(documentSnapshot.getId());
posList.add(m);
}
Log.d(TAG,posList.toString()); //got value
}
});
Log.d(TAG,posList.toString()); //got nothing
}
as you can see when I tried to add value in function onSuccess I got value that has been added but when I tried to add outside function I got nothing
And here is my database structure

You cannot simply use the posList list outside the onSuccess() method because it will always be empty due the asynchronous behaviour of this method. This means that by the time you are trying to print posList.toString() outside that method, the data hasn't finished loading yet from the database and that's why is not accessible. A quick solve for this problem would be to use the list only inside the onSuccess() method, as in the following lines of code:
stores.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
List<GeoPoint> list = new ArrayList<>();
for (QueryDocumentSnapshot document : task.getResult()) {
list.add(document.getGeoPoint("position"));
}
//Do what you need to do with your list
}
}
});
If you want to use that list outside this method, I recommend you see my anwser from this post where I have explained how you can solve this using a custom callback. You can also take a look at this video for a better understanding.

You are getting this error because you are mixing asynchronous calls with synchronous ones. The onSuccess method (Asynchronous) will be called when you receive the data successfully from Firebase. But the onCreateView method will not wait for that. It will directly go to the second
Log.d(TAG,posList.toString()); //got nothing
and you will get nothing (actually I think you'll get an empty string) because you haven't received the data yet.

Related

Android: Infinite loop with LiveData<Boolean>

This gets called when a button is clicked
#Override
public void onFavoriteIconClicked() {
viewModel.isFavoriteExist(test.getId()).observe(getViewLifecycleOwner(), new Observer<Boolean>() {
#Override
public void onChanged(Boolean aBoolean) {
viewModel.isFavoriteExist(test.getId()).removeObserver(this);
if (aBoolean) {
binding.addToFavorite.setImageResource(R.drawable.non_fav);
viewModel.delete(test);
} else if (getActivity() != null) {
Test test2 = new Test(test.getId(), test.getName());
viewModel.insert(test2);
binding.addToFavorite.setImageResource(R.drawable.fav);
}
}
});
}
If the test object exists in the Favorite database, I have to delete it. After deleting, it calls this again (since it observed a chane) and inserts it again.
It keeps looping infinitely. Is there a better way to implement this or stop this?
It seems like some business logic has entered your view (Activity) class.
Since LiveData & Room are meant to be used when receiving updates about Database changes is needed, and your use of the DB is not requiring constant updates, I would suggest going with a more direct approach.
First, Remove the use of LiveData from your Database. Use simple return values.
Your view (Activity/Fragment) can then tell the view model that a button was clicked.
#Override
public void onFavoriteIconClicked() {
viewModel.onFavoriteClicked()
}
The view will observe the view model in order to receive the correct icon to show.
Something like:
viewModel.favoriteIcon.observe(getViewLifecycleOwner(), new Observer<Integer>() {
#Override
public void onChanged(Integer iconResId) {
binding.addToFavorite.setImageResource(iconResId)
}
}
Now the viewModel can handle the logic (or better add a Repository layer - See Here)
Upon click, Check if entry exist in DB.
If exists: remove it from DB and set favoriteIcon value:
favoriteIcon.setValue(R.drawable.non_fav)
If doesn't exist: Add it to DB and set favoriteIcon value.
favoriteIcon.setValue(R.drawable.fav)
For a good tutorial about using Room & LiveData - as well as doing so using the View/ViewModel/Repository pattern, check this link

What is the best way to remove listener in utile class in android?

I have a util class which helps me to get specific data from Firebase database the class like that
public class FirebaseUtils {
private DatabaseReference root;
public FirebaseUtils(){
root = FirebaseDatabase.getInstance().getReference();
}
public void setUerType(Context context,String userid){
DatabaseReference reference = root.child("teachers").child(userid);
reference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()){
PrefsHelper.getInstance(context).setUserType("teacher");
}else {
PrefsHelper.getInstance(context).setUserType("student");
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
}
what is the best way to remove the listener should I creat a method in utile class like that
public void removeListener(){
child.removeEventListener(listener);
}
and call it in onDetach method in the activity?
Yes, but in your case need to create a member of listener's type and not pass anonymous listener's implementation.
As I see in your code, you are using addListenerForSingleValueEvent(), which means that the listener will read the data precisely once. That means that your onDataChange() method gets triggered with the current value (from the cache if available, otherwise from Firebase servers), and stop listening immediately after that. In this case there is no need to remove the listener.
The only time addListenerForSingleValueEvent needs to be canceled is, if there is no network connection when you attach it and the client doesn't have a local copy of the data, either because there was another active listener or because it has a copy of the data on disk.
So in conclusion, there is no need to create a removeListener() method at all.

Spinner NOT Working Well with Firestore's document.getId

Any idea why this doesn't work?
mFirestore.collection("DR1")
.document(UserID)
.collection("Story")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (DocumentSnapshot document : task.getResult()) {
spinnerArray.add(String.valueOf(document.getId()));
}
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
The spinner drop down works (with no default selection, just empty), but on selection, it also does not appear. No error whatsoever. I have setOnItemSelectedListener to Toast.maketext on a selection, but nothing appears as well.
But once I add a:
spinnerArray.add("test");
Before the Firestore database call (the for-loop), then everything works fine. (default selection on "test" on the dropdown list, and when I select another entry, Toast.maketext appears, and selection appears on the spinner)
Thanks again.
This is happening because you are not defining your spinnerArray inside the onComplete() method, which has an asynchronous behaviour. To solve this, move the declaration of your spinnerArray inside the onComplete() method. To display your records you also need to set the adapter inside the method. For more information please see my answer from this post. I also recommend that you see this video, Asynchronous Firebase API - Cloud Firestore and Android, for a better understanding.

How to handle RecyclerView bind synchronous behavior when fetching data

I´m a bit new to this and I have this RecyclerView and I wonder how this will work even do it appear to work at first.
My goal here is to make all View Holders use the same Static method to get data for population the View Holder. I notice It got really expansive to have each View Holder having there own data retrieval code
I have this RecyclerView and when calling the onBindViewHolder like:
#Override
public void onBindViewHolder(SearchPlaceViewHolder holder, int position) {
holder.bind(mList.get(position));
}
In the Adapter I have this static class like;
final static Helper helper = new Helper();
In the View Holder bind I call this Static class like:
helper.getSomeStuff(data, listener);
code:
void getSomeStuff(final Data data, final listener){
DocumentReference ref = FirebaseFirestore.getInstance()
.collection("SOME_COLLECTION")
.document(data.);
ref.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
final DocumentSnapshot document = task.getResult();
if (document.exists()) {
listener.onResult(data, document));
}
}
}
});
}
Since the method firers away an asynchronous call I think maybe the data and listener is not the same when asynchronous return.
It seems to work but I get a bad feeling since all calls to onBindViewHolder happens in same thread (main thread)
so that threads all synchronous calls to helper.getSomeStuff() maybe overwrite data
Any ide?

addValueChangeListener doen't trigger in android

I have a app in building proccess in some where i need to get data from FirebaseDatabase and show them in custom list view here my code part of it for onDataChange method
myDatabase=FirebaseDatabase.getInstance();
myRef= myDatabase.getReference().child("TvSeries");
myAuth = FirebaseAuth.getInstance();
myUser = myAuth.getCurrentUser();
myRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot currentData : dataSnapshot.getChildren()){
if(currentData.child("tCategory").child("tPrimary").getValue().toString().equals("Aksiyon")){ }
selectedCategoryList.add(new DataForRow(currentData.getKey(),
currentData.child("tCategory").child("tPrimary").getValue().toString(),
currentData.child("tReleaseDate").getValue().toString()));
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Integer size =selectedCategoryList.size();
Log.d("Size:", size.toString());
When i put in breakpoint onDataChange method it works but otherwise it didnt any suggestion is very helpful. Have a nice day all.
Your selectedCategoryList list is always empty because onDataChange() method has an asynchronous behaviour which means that is called even before you are try to add those objects of DataForRow class to the list. A quick solve for this problem would be to declare and use your selectedCategoryList list only inside the onDataChange() method or if you want to use it outside, you need to create your own callback and for that, I recommend you see the last part of my answer from this post.
Firebase works asynchronously. You probably got the data from firebase after you program executed the line with Log. As Tristan mentioned, if you put your Log inside of the listener, it will work

Categories