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
Related
In my RegistrationActivity.java class file I've declared a numOfUsers variable to count the number of users in my database.
import ...
public class RegistrationActivity extends AppCompatActivity {
//Other declarations
private static long numOfUsers;
I've created an event listener for checking if a particular user exists in the database. Inside this event listener there is another event listener which counts the total number of users in the database.
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(!dataSnapshot.exists()) {
//create new user
database.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
numOfUsers = dataSnapshot.getChildrenCount();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
//Displaying the numOfUsers in app
userinfo.setText(String.valueOf(numOfUsers));
}
This prints 0. If I place userinfo.setText(String.valueOf(numOfUsers)); inside the second event listener then everything works fine.
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(!dataSnapshot.exists()) {
//create new user
database.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
numOfUsers = dataSnapshot.getChildrenCount();
//This works perfectly fine
userinfo.setText(String.valueOf(numOfUsers));
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
I don't understand why this is happening. numOfUsers is a static class variable I should be able to access it's value from anywhere inside the class. Is there a way I can't print numOfUsers outside the second event listener?
It's not about where you access the data, but about when you access it.
Data is loaded from Firebase (and most modern cloud APIs) asynchronously, since it may take some time to get back.
Instead of blocking your main thread (which would lock out the user), your main code actually continues to run while the data is being loaded. Then when the data is available, your onDataChange gets called with it.
This means that in your code the userinfo.setText(String.valueOf(numOfUsers)) outside of onDataChange runs before onDataChange ever executes and thus passes the wrong value to the text view.
The solution for this is always the same: any code that needs the data from the database must be inside the onDataChange, or be called from there.
This is an incredibly common source of confusion, so I recommend reading more on:
getContactsFromFirebase() method return an empty list
Setting Singleton property value in Firebase Listener
Get the whole values from one key in firebase
Retrieve String out of addValueEventListener Firebase
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.
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.
I am kinda new to Android programming. I am building a simple application and I have a "follow-unfollow" concept on it. What I simply want is, if the current user follows the user he/she is exploring, I want him/her to see "unfollow" button. If not following, there should be a "follow" button. On my UserProfileActivity class I have a method called onPrepareOptionsMenu() and inside this method I can set the buttons.
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem follow = menu.findItem(R.id.action_follow);
MenuItem unfollow = menu.findItem(R.id.action_unfollow);
Bundle bundle = this.getIntent().getExtras();
if(isFollowing(bundle.getString("userid")) == true){
follow.setVisible(false);
unfollow.setVisible(true);
}
else{
follow.setVisible(true);
unfollow.setVisible(false);
}
return super.onPrepareOptionsMenu(menu);
}
Also, I have another method called isFollowing() and it returns a boolean "true" if current user follows the other user, it returns "false" if not. It is the simplest way that I have thought to solve this issue.
public boolean isFollowing(String userID){
isFollowingResult = false;
firebaseDatabase = FirebaseDatabase.getInstance();
databaseReference = firebaseDatabase.getReference();
final DatabaseReference followingData = databaseReference.child("followingData");
followingData.child(currentUser.getUid()).child(userID).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists())
isFollowingResult = true;
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return isFollowingResult;
}
When I run this, and when I click on a user's profile onPrepareOptionsMenu() method is called and inside it isFollowing() method is called. The problem is, it does not wait for the isFollowing() method to run and finish running and it immediately sees it as "false" and always shows "follow" button. Any suggestion would be appreciated.
Thanks.
Make follow and unfollow the fields of the class. Then change their visibility in onDataChange(DataSnapshot) method.
There are multiple issues with the code.
Firstly, your isFollowing() function is setting up the listener, on the data field, but it'll only get called when the data changes. In this case, you may only want to read the data once:
https://firebase.google.com/docs/database/android/read-and-write#read_data_once
Secondly, the use of a listener implies asynchronicity. Meaning, you'll need to wait until you get the callback later in order to get the value you want.
The ideal solution in order to maintain responsiveness of your app is to maintain a local "copy" of the value in your database with a listener that constantly updates that value. That way, you can query the state of your variable quickly (since it's stored/replicated locally) and still be up to date with your database (with the listener).
This will also prevent each "read" from going all the way to the service and back and also remove the need for your UI to wait to render correctly (accurately).
so the Code looks like this right now.
The Problem is that I get more than one Logprints. But I need only the full and finished meberList.
for (String memberByNumber : memberListByNumber) {
mFirebaseDatabaseReference.child("userUidsByNumber/").child(memberByNumber).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
String userUid = dataSnapshot.getValue().toString();
memberList.add(userUid);
Log.d("LOL", "ContactsAdapter: " + memberList);
} else {
Snackbar.make(view, R.string.user_does_not_exist, Snackbar.LENGTH_LONG).show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Snackbar.make(view, R.string.error, Snackbar.LENGTH_LONG).show();
}
});
}
I hope you understand my Problem and can help me.
Thanks in advance.
If onDataChange gets called for every member for memberListByNumber, then you can try
final int total = memberListByNumber.size();
for (String memberByNumber : memberListByNumber) {
mFirebaseDatabaseReference
.child("userUidsByNumber/")
.child(memberByNumber)
.addValueEventListener(new ValueEventListener() {
int n = total;
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
String userUid = dataSnapshot.getValue().toString();
memberList.add(userUid);
if (--n <= 0) Log.d("LOL", "ContactsAdapter: " + memberList);
} else {
Snackbar.make(view, R.string.user_does_not_exist, Snackbar.LENGTH_LONG).show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Snackbar.make(view, R.string.error, Snackbar.LENGTH_LONG).show();
}
});
}
I want to comment it but due to my reputation here I couldn't
So, here is my answer your logging every entry into one file so that it just makes printing the necessary entries much harder So go on different path and give one more if-else statement in existing one and use try-exception to divide the finished and canceled into some cache memory and then void the finished file
In firebase, there's never a thing as complete list.
Value events keep happening whenever any data changes under any of the child nodes. Also value events happen last ie after all child events are finished ensuring all children nodes at that point are present. Next event will give you the complete list at that particular point.
On each data change call, you can clear the list, add all the new values and print that. This is how firebase works.
If you want the entire list only once and don't want any new changes, you can add the value event listeners using addListenerForSingleValueEvent method. Now the list will be read once and no more. However if you have persistence enabled, the data will be read once from the phone cache and not from the db
Maybe what you are looking for is a ChildEventListener which will give you more granular control. This will have child added, deleted and changed events which will give you the list data one child at a time.
I don't know what your actual purpose is so this is the best i can try to explain. Hope this helps