mRefQ.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String txt = dataSnapshot.getValue(String.class);
showtext.setText(txt);
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
as it stands i can only access the value when the data is changed but i want to access it whenever i want(namely oncreate).
From the Firebase documentation for listening to value events:
This method is triggered once when the listener is attached and again every time the data, including children, changes.
You can access the Data only once by following this steps.
As #Frank mentioned having a listener will give you access when attached and every time it changes.
Bear in mind that all of this happens asynchronously on a separate thread, therefore in order to ensure the value is available on onCreate you may have to do the orchestration yourself and hold the main thread until the values is ready to be used.
Alright so it turns out it does call it when created, i just had to wait for the firebase to load up on the app
It is not triggerred once in onCreate() method. Without removeEventListener, addValueEventListener will never stop if you listen it in OnCreate() Method. Count is increasing and never stops and load was up to 85% when I run it and so I had to close my internet and uninstall it. But ienter image description heret has just been solved. Here's my code if you wanna check it.
Variables are declared global...
private DatabaseReference main;
private ValueEventListener valueEventListener;
In onCreate() Method.....
valueEventListener = main.addValueEventListener(new
ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
boolean countExists = false;
int count = 1; //starts from 1 when data is uploaded for the first time
for (DataSnapshot out: dataSnapshot.getChildren()) {
if (out.getKey().equals("count")) {
String temp = out.getValue(String.class);
countExists = true;
try {
count = Integer.parseInt(temp);
}
catch (NumberFormatException e) {
count = 1;
}
break;
}
}
if (!countExists) {
main.child("count").setValue(String.valueOf(count));
Toast.makeText(getApplicationContext(), "Count Created", Toast.LENGTH_SHORT).show();
upload.setClickable(true);
}
else {
main.child("count").setValue(String.valueOf(++count));
Toast.makeText(getApplicationContext(), "Count Updated", Toast.LENGTH_SHORT).show();
upload.setClickable(true);
}
main.removeEventListener(valueEventListener);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(getApplicationContext(), "Failed to upload", Toast.LENGTH_SHORT).show();
}
});
Make sure you add removeEventListener when you call it in onCreate() method or it will never stops. It is not a problem if you don't call removeEventListener in onStart() method.
This is not your question but...
If you wanna use firebase for likely free, you can't let all users to download your data without necessary or your bandwidth may become full within a day. So, I count the data and just download that child's value and compare the result and make update. So it can reduce your bandwidth when you call addValueListener.
Related
I am trying to add online/offline feature in my android chat app using .info/connected path
I wrote the following code inside onCreate() method
studentref = FirebaseDatabase.getInstance().getReference("student").child(user.getUid());
connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected");
connectedRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
boolean connected = snapshot.getValue(Boolean.class);
if (connected) {
studentref.child("status").onDisconnect().setValue("offline");
studentref.child("status").setValue("Online");
} else {
studentref.child("status").setValue("offline");
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
But the else part does not execute when i minimized the app for more than 60 seconds
It only works when i killed the app or when i switch off the internet for more than 60 seconds
How to make it works , when the app is in foreground it should set the value "online" and when the app is in background or killed it should set the value to "offline"
I solved this issue using 2 ways
First we need to detect when the app goes to the background and come back to the foreground , so when the app goes to foreground update the user state as "Online"
when the app goes to background update the user state as "Offline"
We can achieve this by implementing LifecycleObserver class and writing the following 2 methods
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onAppBackgrounded() {
//App in background
// here update user state as offline
}
#OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onAppForegrounded() {
// App in foreground
// here update user state as online
}
But this will not work when there is no internet connection , Eg: when the user switch off the internet while the app in foreground and close the app in this case the user state will remain online even the app is closed , To solve this we need also to check the connection to .info/connected path
connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected");
listenerCon = new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
boolean connected = snapshot.getValue(Boolean.class);
if (connected) {
user= FirebaseDatabase.getInstance().getReference("users").child(FirebaseAuth.getInstance().getCurrentUser().getUid());
user.child("state").setValue("Online");
user.child("state").onDisconnect().setValue(ServerValue.TIMESTAMP);
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
};
connectedRef.addValueEventListener(listenerCon);
So when there is no connection for more than 60 sc the user state will be updated to offline
The .info/connected reflects the connection of your application to the backend servers of the Firebase Realtime Database. While this may have some relation to whether the user is actively using the app, it is not a 1:1 mapping.
If you want to detect whether the app is backgrounded, there are better ways (unrelated to Firebase) such as How to detect when an Android app goes to the background and come back to the foreground and others from these search results.
Use my code, in onCreate
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
String id = user.getUid();
DatabaseReference reference = FirebaseDatabase.getInstance().getReference().child("Users").child(id).child("stateOnline");
if (user != null){
reference.setValue(true);
reference.onDisconnect().setValue(false);
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
boolean connected = snapshot.getValue(Boolean.class);
if (connected) {
// CONNECTED
} else {
// NOT CONNECTED
}
}
#Override
public void onCancelled(DatabaseError error) {
System.err.println("Listener was cancelled");
}
});
reference.keepSynced(true);
}
My Firebase Realtime Database has been built by loading an object of the Java class HashMap. In my Android Studio app I'm trying to write a method that takes a String (the key) as input, searches through the database and if the string is found it returns the associated Float (the value), otherwise it returns 0. How can I do this? Any help would be really appreciated!
EDIT: I've tried to follow the suggestions, adapting them to my particular case, but I didn't manage to solve the problem yet.
I wrote the following code in MainActivity:
DatabaseReference myRef;
Float tempValue;
#Override
protected void onCreate(Bundle savedInstanceState) {
...
myRef = FirebaseDatabase.getInstance().getReference("myRoot");
tempValue=0f;
...
}
public void retrieveValueFromDatabase(String childName, final MainActivity activity){
myRef.child(childName).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Float value=dataSnapshot.getValue(Float.class);
if (value==null){
value=0f;
}
activity.tempValue=value;
//First Toast
//Toast.makeText(activity,"tempValue = "+tempValue.toString(), Toast.LENGTH_LONG).show();
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
});
}
public void useValues(){
retrieveValueFromDatabase(childName,this);
//Second Toast
//Toast.makeText(this,"tempValue = "+tempValue.toString(), Toast.LENGTH_LONG).show();
//code using tempValue from here
...
}
If I uncomment the first toast, the correct value inside tempValue is shown, but if I uncomment the second toast, the value of tempValue shown is the default one (0.0). What am I missing?
You need to use addValueEventListener to retrieve data from the database:
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("myRoot").orderByChild("name").equalTo("peter");
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.i("Database", dataSnapshot.child("floatValue").getValue(Long.class));
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
})
Here, you add a reference to the root node, then query using equalTo() to check if name = peter exists in the database and return the float value.
You should read the guide:
https://firebase.google.com/docs/database/android/read-and-write
My app has an activity that shows a news article and it has a bookmark ImageButton. When clicked, the method shown below is called
public void onBookmarkClick(View view){
if (isBookmarked){
bookmarkedDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()){
Article articleValue = snapshot.getValue(Article.class);
String id = snapshot.getKey();
if (article.equals(articleValue)){
bookmarkedDatabase.child(id).removeValue();
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}else {
String id = bookmarkedDatabase.push().getKey();
bookmarkedDatabase.child(id).setValue(article);
}
isBookmarked = !isBookmarked;
setupBookmarkIcon();
}
But when I rotate the screen or press back button immediately after clicking bookmark button, the article isn't added to bookmarks.
How do I make sure that the process of reading and writing from/ to a firebase database is complete even when activity is being destroyed.
You can use complete Listener to know if your update is successful or not . From firebase documentation
Add a Completion Callback
If you want to know when your data has been committed, you can add a completion listener.
Both setValue() and updateChildren() take an optional completion
listener that is called when
the write has been successfully committed to the database.
If the call was unsuccessful, the listener is passed an error object indicating why the failure occurred
Firebase
Or my suggestion to use viewModel class and do update from there.
Viewmodel
I am trying to make it so that when a user creates an open game, in my Firebase Database the name of the game is the size of the list. So the very first game created the name of it will = 0, and if another user creates a game then the game will be labeled 1 and so on.
I have it set up right now so that the games are labeled the size of the game list, but the list isn't really updating. The games keep getting called '0' because it thinks the list is empty even though I have visual confirmation in the app that there are items being added to the list.
So my question is: How can I make it so the list continuously updates each time a game is added, and how can I make it so that it updates for all users and not just the user who created the game?
This is what I have setup right now. Here are the variables I am using for the list and the integer getting the list size
ArrayList<String> openGames = new ArrayList<>();
int gameSlot = openGames.size();
Here is what I use to name the game when it is created.
gameMaker = new GameMaker(hp.uid, userName, wagerD, gameSlot);
FirebaseDatabase.getInstance().getReference("FCGames").child(Integer.toString(gameSlot))
.setValue(gameMaker).addOnCompleteListener...
And this is what I have to add the game to the list.
cgRef.child(Integer.toString(gameSlot)).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
openGames.add(userName);
adapter.notifyDataSetChanged();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
So again my question is how can I make this list update correctly and how can I make it update for all users on the app?
Edit
Here is what I did with my onChangeData
cgRef.child(Integer.toString(gameSlot)).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
wager = (String) dataSnapshot.child("wager").getValue();
gameSlot = openGames.size();
adapter.notifyDataSetChanged();
}
and now the openGames.add is in my createGameLobby method.
FirebaseDatabase.getInstance().getReference("FCGames").child(Integer.toString(gameSlot))
.setValue(gameMaker).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
openGames.add(userName);
Toast.makeText(FlipCoinLobby.this, "Game creation successful.", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(FlipCoinLobby.this, task.getException().getMessage(), Toast.LENGTH_SHORT).show();
}
}
});
^^ that is just the important snippit from the method. And then I have an onClickListener that creates that calls that method when a button is pressed
In below code segment, you did that openGames.add(username) in onDataChange listener. I think it is an incorrect use of this ondatachange function
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
openGames.add(userName);
adapter.notifyDataSetChanged();
}
please use this to get data from db and update your data. Then push your data to db. You didn't use snapshot value also. You can get most recently updated data from dataSnapshot . Use it to update user data then push it to db
When a user makes an account with firebase on my android app, they then need to be directed to a screen where they can add some separate information like username, for example. The app knows to switch the activity through an AuthStateListener, so when an account is made or someone signs in the code here is executed (no intents are changed here right now, keep reading to see why):
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
// User is signed in
Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
userID = user.getUid();
toastMessage("User has signed in: " + user.getEmail());
} else {
// User is signed out
Log.d(TAG, "onAuthStateChanged:signed_out");
}
// ...
}
};
In the if block where it is checking to see if user does not equal to null, this is where I need to change the intent to another activity where they will enter their username. Basically the logic is, if the user already has a username and they are just signing in then they will be directed to the home screen activity, and if they are signing up they need to be directed to the activity where they make a username. To know which activity I need to send them to I need to have the code look in the firebase database and see if their account has a username already or not. I cannot figure out how to read data from the firebase in this situation, it seems as if the only way to read from the database (according to others) is through an onDataChange methods like so:
mRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d(TAG, "This: "+dataSnapshot.getValue());
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
which uses dataSnapshots. the problem with this is this code is only fired when data is added to the database, therefor this method will not execute any code when a user is simply signing up and has never added a username to the database. How can I query to see data from the database just to check if a certain user has any data there?
OnDatachange is fired in all of cases, if user add data, remove data, change data ect.
To check is user already have username do this inside OnDatachange:
if(dataSnapshot.exists()){
//GO TO OTHER ACTIVITY
]else{
//GO TO SET USER DATA ACTIVITY
]
See this exemple of one of my implementations:
if (user !=null){
DatabaseReference myRef = database.getReference("users").child(user.getUid()).child("user_bio");
myRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()){
bio.setText(dataSnapshot.getValue(String.class));
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
In this exemple if user_bio exists then set text to userbio if not do nothing. In the first time that user log in in my app this method do nothing but when user add bio this method set the bio text in userbio text. but if i want to show some text if user hasnt bio i simply add na else and set text to the bio for exemple:
if (user !=null){
DatabaseReference myRef = database.getReference("users").child(user.getUid()).child("user_bio");
myRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()){
bio.setText(dataSnapshot.getValue(String.class));
}else{
bio.setText("no user bio");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Have you try to use addValueEventListener inside onCreate method?
If you run an activity while the listener inside onCreate method it will fired once when you open an activity. So i think you shouldn't have a problem here.