I can't get to work this code to read (getUser method) from my Firebase DB. I have searched for answer for 2 hours, tried different tutorials and nothing helped. Problem is that it looks like the onDataChange method is never actually called (tested with Log) and I don't know what am I doing wrong. Writing to DB (saveUser method) is working as it should.
Code:
public class UserDatabase {
private User user;
private DatabaseReference databaseReference;
public UserDatabase() {
databaseReference = FirebaseDatabase.getInstance().getReference();
}
public void saveUser(User user) {
databaseReference.child("users").child(user.getUid()).setValue(user);
}
public User getUser(String uid) {
databaseReference = FirebaseDatabase.getInstance().getReference().child("users").child(uid);
databaseReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
user = dataSnapshot.getValue(User.class);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return user;
} }
Thanks for answer
As I see in your comment, you are saying that "eventually gets the data" but I tell you that the way in which you are trying to use the getUser() method which has as a return type the User class, is not the correct way to solve this problem, especially when it comes to asynchronous methods. You cannot return something now that hasn't been loaded yet. onDataChange() method has an asynchronous behaviour. A quick fix to your problem would be to use those user objects that you are getting from the database only inside onDataChange() method or, if you want to use those objects outside, please see the last part of my answer from this post, in which I explain the way in which you can use a callback in order to achieve this.
Related
So I have firebase initialised in my acitivity.
mFirebase = FirebaseDatabase.getInstance();
mDatabaseReference = mFirebase.getReference("buses");
mBusReference = mDatabaseReference.child(mSelectedBusModel.getRegistrationNo());
mValueEventListener = new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
...
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.e("Data", "Cancelled");
databaseError.toException().printStackTrace();
}
}
mBusReference.addValueEventListener(mValueEventListener);
I have a singleton that extends Application and inside the onCreate method I have enabled firebase persistence:
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
Now my problem is when I go offline and reopen my activity the data has not been cached, nothing loads. I have read the docs and done everything mentioned but it still does not work.
What I want to do is enable my app to cache data already loaded from firebase so it's always available even after the app has been closed and re-opened.
Please note no errors are displayed in my console.
Just using setPersistenceEnabled(true) doesn't cut it. You must change your listener because you save the data in your disk but you dont handle them. You must use something like this (it was taken from firebase documentation i have used it for my app and worked for me).
--- edit ---
If you want to keep your data synced you must use ref.keepSynced(true);. The below piece of code is what i used to retrieve cached data from reference. When i am offline the child returns the values normally.
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("user");
ref.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot snapshot, String previousChild) {
System.out.println(snapshot.getValue());
}
});
I've got a Firebase Database with User data. And I have a User class with this method:
private void getFromFirebase(){
final FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference reference = database.getReference( FirebaseReferences.USERS ).child( userId );
reference.addValueEventListener( new ValueEventListener(){
#Override
public void onDataChange( #NonNull DataSnapshot dataSnapshot ){
display_name = dataSnapshot.child( "username" ).getValue( String.class );
photo = dataSnapshot.child( "profile_image" ).getValue( String.class );
}
#Override
public void onCancelled( #NonNull DatabaseError databaseError ){
}
} );
}
In the MainActivity.java, I want to get the profile photo from the database and show it in an ImageView. Problem is that Firebase is asynchronous and it returns immediately, so I can't just call myUser.getFromFirebase() because myUser will still have all null values. I searched a lot but can't find the solution, since I don't want my User class to interact at all with the UI (I want to use a 3-tier methodology).
I tried creating an AsyncTask extension class, but it has the same problem, because the issue is at the User class. I also tried the CountdownLatch approach, but since the value is already on the database, the onDataChange method never gets called at all!!
Does anyone has any idea how to solve this? I'm sure it's extremely easy, because it's not a weird scenario, but I'm so stucked...
Create callback listener, like below
public interface OnDataReceiveCallback {
void onDataReceived(String display_name, String photo);
}
Modify method to pass callback
private void getFromFirebase(OnDataReceiveCallback callback){
final FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference reference = database.getReference( FirebaseReferences.USERS ).child( userId );
reference.addValueEventListener( new ValueEventListener(){
#Override
public void onDataChange( #NonNull DataSnapshot dataSnapshot ){
display_name = dataSnapshot.child( "username" ).getValue( String.class );
photo = dataSnapshot.child( "profile_image" ).getValue( String.class );
callback.onDataReceived(display_name,photo);
}
#Override
public void onCancelled( #NonNull DatabaseError databaseError ){
}
});
}
Final call of getFromFirebase
getFromFirebase(new OnDataReceiveCallback(){
public void onDataReceived(String display_name, String photo){
// do something
}
});
You will have to arrange for your views to be updated with each callback you receive to onDataChange. If you don't want your callback to directly modify views, you will need to adopt some form of app architecture to abstract your repository (Realtime Database) from your views.
This is not "extremely easy". Also, you have a lot of choices for app architecture (MVP, MVC, MVVM), and various frameworks to help with this (such as Android's own LiveData). What you are venturing into is highly opinionated, and involves writing a lot more code than you have here.
I can point you to a repository that uses Jetpack's Android Architecture Components as app architecture for a demo app that uses both RTDB and Firestore, but you'll see that it's a lot of lines of code, and it's also just my opinion about how to get things done. You will find lots of other opinions out there.
I have users on my Application and I store additional information about them in Firebase Database. I need to retrieve additional information in more than one Activity. I do not want to use ValueEventListeners because they are not called unless there is any change in the database. How can I get information about users from Database without using ValueEventListeners?
In my ProfileFragment I need to get name and departmant values.
I get current user from Firebase and I tried to take other information with a function.
talker = new DatabaseTalk();
FirebaseUser currentUser = mAuth.getCurrentUser();
// Get info of logged in user with talker.
loggedInUser = talker.getUserFromID(currentUser.getUid());
This is my DatabaseTalker class to handle read and write operations to database
public class DatabaseTalk {
private FirebaseDatabase mDatabase;
private DatabaseReference UserRef;
private DatabaseReference SurveyRef;
private List<User> userList;
public DatabaseTalk(){
mDatabase = FirebaseDatabase.getInstance();
UserRef = mDatabase.getReference("users");
UserRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot child: dataSnapshot.getChildren()){
userList.add(child.getValue(User.class));
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w("Error", "Failed to read value.", databaseError.toException());
}
});
SurveyRef = mDatabase.getReference("surveys");
}
public void WriteUser(User usr){
UserRef.child(usr.getUserID()).setValue(usr);
}
public void WriteSurvey(Survey survey){SurveyRef.push().setValue(survey);}
public User getUserFromID(String id){
for(User usr: userList){
if(usr.getUserID().equals(id))
return usr;
}
return null;
}
}
I think, I can take additional information about users from userList in DatabaseTalk but userList is null always.
EDIT
I changed getUserFromID method. OnDataChange() does not work when I called getUserFromID method.
public User getUserFromID(String id){
DatabaseReference newRef = mDatabase.getReference("users");
DatabaseReference ds = newRef.child(id);
ds.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
userList.add(dataSnapshot.getValue(User.class));
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return userList.get(0);
}
I solved the problem. It turned out I do not know how Firebase works actually. Since onDataChange() make async calls, writing Listener definitions on a function is useless because onDataChange mostly does not trigger before function terminates and this cause function to return null value.
I make the definition of ValueEventListeners in onCreate methods. It triggers now after few seconds my ProfileFragment created. I think it is better to use Progress Dialogs to wait.
Thanks to everyone who interested in the question.
I keep getting the old values (which no longer is in the database) from my Firebase database. Here is how the database looks right now:
I am getting the info under friendlist. It used to be only one child there with key-value set soosk: true, but now it looks like in the photo. When using addListenerForSingleValueEvent() to my databaseRef, the friendlist retrieved only has soosk: true in it. Here is my code:
mFirebaseDatabaseReference = FirebaseDatabase
.getInstance()
.getReference(
"users/"+FirebaseAuth.getInstance().getCurrentUser().getUid()
);
mFirebaseDatabaseReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
Log.d(TAG, user.getFriendlist().toString());
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
The method you need to use to have all the values updated is addValueEventListener(). Because you need to listen for more than one value this is the only way to achieve it and to use addListenerForSingleValueEvent().
The most important thing is to remove the listener in your onDestroy method like this:
yourReference.removeEventListener(valueEventListener);
I have recently been working with Android development. I have been developing a social networking app. For the app, I decided to create a separate helper class for all database methods. In my database, all users have a user id and their information is stored under this id. I have a (non-static) method in this class that would get certain User information when given a DatabaseReference to the user's information location. The method would simply take the reference, add a listener for single value event (addListenerForSingleValueEvent(ValueEventListener)). I was encountering problems with this so I tried putting a Log statement in the onDataChange() method of the ValueEventListener. Oddly enough, this Log method was never reached. Even more strange is the fact that, if I copy and paste the code from this method into one of the locations where I need it, the Log statement is reached. Does anyone have any idea as to why this happens? This is a method that I am using in multiple activities and copying and pasting the code everywhere would make the code very sloppy. Any help would be much appreciated. Thank you!
Update: It turns out the code works if placed in the Database class, but the Log statement will only run after the method is over. Below is the an outline of the class I am using to observe this.
Fragment Class
public class FragmentClass extends Fragment {
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDatabase = DatabaseManager.getInstance();
String userId = "userId";
mDatabase.getUserFromUserId(userId);
}
}
Database Class
public class DatabaseManager {
private static FirebaseDatabaseManager mInstance;
private static FirebaseDatabase mDatabase;
public static FirebaseDatabaseManager getInstance() {
if(mInstance == null) {
mInstance = new FirebaseDatabaseManager();
}
return mInstance;
}
private FirebaseDatabaseManager() {
mDatabase = FirebaseDatabase.getInstance();
}
public void getUserFromUserId(final String userId) {
DatabaseReference userReference = mDatabase.getReference(userId);
userReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.i("databaseTag", "reached");
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.i("databaseTag", "reached");
}
});
while(true) { // if this part is commented out, the log statement will be executed; otherwise, it won't
}
}
}