I created A method that could be able to get the data in my firebase using the userId when I debugging it, it couldn't be able to go inside am wondering whats wrong with my code. This is my code:
public static String GetUserRole(final String userId){
Role = null;
getUsersRef().child(userId).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String role = dataSnapshot.getValue().toString();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return Role;
}
is there any problem with this?
When implementing the ValueEventListener, the overridden methods are called when an event happens. this means that it isn't called and executed when the GetUserRole method is executed, but it is called separately, when the event happens (data received from Firebase). If you print a log you will see that the onDataChange method or the onCancelled method will be called (after GetUserRole run).
Try adding a Log print like this:
getUsersRef().child(userId).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d(TAG, "onDataChange!");
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d(TAG, "onCancelled!");
}
});
Related
I'm trying to get a specific user from the database but I can't, also I would like to check if there is already a node with the same name (uid). I don't want to get the whole list of users and then retrieve one from it because I don't think that's good for perfomance, my idea is to directly get the user from the database
I tried many ways but everything I try returns null, I can't get the values from dataSnapshot. I use an interface to make sure I execute the code after the reading is done
currentUser = FirebaseAuth.getInstance().getCurrentUser();
mUserReference = FirebaseDatabase.getInstance().getReference("users/"+currentUser.getUid());
mUserReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
user = dataSnapshot.getValue(User.class);
dataStatus.dataIsLoaded(user);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
Here the interface
public interface DataStatus{
void dataIsLoaded(User user);
}
And when I try to get the user it shows NullPointerException
UserUtils userUtils = new UserUtils();
userUtils.getUser(new UserUtils.DataStatus() {
#Override
public void dataIsLoaded(User user) {
mUser = user;
}
});
User creation, when I call this method I do uid = currentUser.getUid()
public void createNewUser(String uid, String name, String email, DataStatus dataStatus) {
mUserReference = mDatabase.child("users").child(uid);
mUserReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
user = new User(name, email);
mDatabase.child("users").child(uid).setValue(user);
dataStatus.dataIsLoaded(user);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
Here the rules
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
Here's how my data base is organized
SOLVED
The problem was a piece of code, which I didn't include here, that was trying to get access to the user outside the dataIsLoaded interface method, and as onDataChange is asynchronous the user hadn't been downloaded yet so it was conflicting with the code.
Welcome to SO community
Please use .child() method instead of cascading nodes with "/" as below
Also check if the value of the datasnapshot is not null using .exists() method
currentUser = FirebaseAuth.getInstance().getCurrentUser();
mUserReference = FirebaseDatabase.getInstance().getReference.child("users").child(currentUser.getUid());
mUserReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()) {
user = dataSnapshot.getValue(User.class);
dataStatus.dataIsLoaded(user);
}
}
// rest of code
Feel free if you need further support
UPDATE
Please replace your listener with below
currentUser = FirebaseAuth.getInstance().getCurrentUser();
mUserReference = FirebaseDatabase.getInstance().getReference.child("users").child(currentUser.getUid());
Query query = FirebaseDatabase.getInstance().getReference.child("users").orderByKey()
.equalTo(currentUser.getUid());
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot singleSnapshot : dataSnapshot.getChildren()) { // dataSnapshot contains set of returned result of the query
User user = singleSnapshot.getValue(User.class);
if (user != null) {
dataStatus.dataIsLoaded(user);
}
}
}
}
Welcome to SO community.
When did you call your function to read the user content?
it might be an asynchronous problem.
To make sure, make a function where the user is the parameter and call it once you read something from the DB (onDataChange).
Plus, did you try to print out what is inside " user " when you read it? if it gives you null, the problem is within the path for sure. If so, use .child("users").child(currentUser.getUid())
i want to check id duplication
i want to make id duplication check code.
so i get the id which is typed from user by using sid = id.getText().toString().trim();
and then make json trees FirebaseDatabase.getInstance().getReference().child("users").child("id").setValue("add");
by using child() method ...
and put the value using setvalue() method.
because it was not possible to use ondatachange() method... (i think this method only works when there is data so i put some no useful data) i will remove the value which is in setvalue() method later (is there another method that can be used even though there is no change... i need to check the db contents)
anyway if (data.getValue()==sid) this part
i thought it can check the id ...
but it does not work
12-25 21:06:00.771 6700-6700/com.example.pc.login D/myTag: {id=add}
android log looks like this... id is the child part... and the add is the value that i put
that is the result of the getvalue() method...
summary
:
1. how can i put the id from users to the firebase db
2. how can i get the only id value except child part
when i use getvalue() method. it takes child name and the value that i put
so if the id is exist ...user made ... user should write another...
if not... the firebase db store the id user made...
thank you for reading ... help me...
idcheckbtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//store the id user created
sid = id.getText().toString().trim();
FirebaseDatabase.getInstance().getReference().child("users").child("id").setValue("add");
FirebaseDatabase.getInstance().getReference().addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot data: dataSnapshot.getChildren()){
if (data.getValue()==sid) {
Log.d("myTag",""+dataSnapshot.getChildren());
Toast.makeText(MainActivity.this, "aleady exists",Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "you can use this id",Toast.LENGTH_SHORT).show();
FirebaseDatabase.getInstance().getReference().child("users").child("id").setValue(sid);
Log.d("myTag",""+data.getValue());
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
});
because it was not possible to use ondatachange() method... (i think this method only works when there is data so i put some no useful data)
No onDataChange(DataSnapshot dataSnapshot) method is triggered once when the listener is attached, whether there is data or NOT.
If the database path where the ValueEventListener is attached is empty/null/non-existent than dataSnapshot.exits() will return false.
Hence here can be your final code:
idcheckbtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
sid = id.getText().toString().trim();
// No need to store the id user created
// FirebaseDatabase.getInstance().getReference().child("users").child("id").setValue("add");
FirebaseDatabase.getInstance().getReference().child(sid).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
// sid already exists
} else {
// sid does not exists already
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
});
By the way your Firebase Database will look something like this:
{
"sid_1": "...",
"sid_2": "..."
}
I'm trying the add the retrieved values from Firebase database to an Arraylist and from there to a String array. My retrieval method works fine. I can have all the values printed out in a toast. But apparently it doesn't get added to the arraylist.
Here's my code for retrieval in onActivityCreated() of fragment class.
ArrayList<String> allBrands = new ArrayList<>();
brandRef=FirebaseDatabase.getInstance().getReferenceFromUrl("https://stockmanager-142503.firebaseio.com/Brands");
q=brandRef.orderByChild("brandName");
q.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
allBrands.add((dataSnapshot.getValue(Brand.class)).getBrandName());
Toast.makeText(getActivity(),(dataSnapshot.getValue(Brand.class)).getBrandName(), Toast.LENGTH_SHORT).show();
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
And this is where I'm trying to use the arrayList in OnActivityResult() method of the Fragment class but the iterator loop is not executed I believe. The toast is not seen. I'm getting a null pointer exception when I try to work with the array. I assume the values do not get copied to the brands array.
count=allBrands.size();
String[] brands=new String[count];
Iterator<String> itemIterator = allBrands.iterator();
if(itemIterator.hasNext()){
//brands[i] = itemIterator.next();
Toast.makeText(getActivity(), itemIterator.next(), Toast.LENGTH_SHORT).show();
// i++;
}
for( i=0;i<count;i++){
if(brands[i].compareTo(Brand)==0){
f=1;break;
}
}
Here's my database in case that helps. But I can print out all the retrieved values in a Toast with no problem.
It's hard to be certain from the code you shared, by I suspect you may be bitten by the fact that all data is loaded from Firebase asynchronously. Alternatively you may simply not have permission to read the data. I'll give an answer for both.
Data is loaded asynchronously
It's easiest to understand this behavior when you add a few log statements to a minimal snippet of your code:
System.out.println("Before attaching listener");
q.addChildEventListener(new ChildEventListener() {
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
System.out.println("In onChildAdded");
}
public void onChildChanged(DataSnapshot dataSnapshot, String s) { }
public void onChildRemoved(DataSnapshot dataSnapshot) { }
public void onChildMoved(DataSnapshot dataSnapshot, String s) { }
public void onCancelled(DatabaseError databaseError) { }
});
System.out.println("After attaching listener");
The output of this snippet will be:
Before attaching listener
After attaching listener
In onChildAdded (likely multiple times)
This is probably not the order you expected the output in. This is because Firebase (like most cloud APIs) loads the data from the database asynchronously: instead of waiting for the data to return, it continues to run the code in the main thread and then calls back into your ChildEventListener.onChildAdded when the data is available.
There is no way to wait for the data on Android. If you'd do so, your users would get the daunted "Application Not Responding" dialog and your app would be killed.
So the only way to deal with the asynchronous nature of this API is to put the code that needs to have the new data into the onChildAdded() callback (and likely into the other callbacks too at some point):
q.addChildEventListener(new ChildEventListener() {
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
allBrands.add((dataSnapshot.getValue(Brand.class)).getBrandName());
System.out.println(allBrands.length);
}
You need permission to read the data
You need permission to read the data from a location. If you don't have permission, Firebase will immediately cancel the listener. You need to handle this condition in your code, otherwise you'll never know.
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
Try this (I'm writing this to future reference of myself .. too)
As you can see we implement a reresh before it's end. There is probably a nicer way to do it. However, it is not documented. Also all this event Listners should be add autmoatically and released automatically by firebase but they don't do it from some reason.
/**
* #param uid User's ID
* #param Callable send as null just to implement a call to assure the callback is updapted before it's finished
* #return returns ArrayList of all Games unique identification key enlisted in a User
*/
private final ArrayList<String> mGamesPlaying = new ArrayList<>();
public ArrayList<String> mGamesPlaying(final String uid, final Callable refresh) {
final Firebase ref = FirebaseRef;
Firebase usersRef = ref.child("users").child(uid).child("playing_games");
usersRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
mGamesPlaying.clear();
for (DataSnapshot child : snapshot.getChildren()) {
Log.d(TAG, "Test Game" + child.getKey());
mGamesPlaying.add(child.getKey());
}
try {
refresh.call();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
return mGamesPlaying;
}
Is there a way to perform a simple query on a Firebase database that gets and returns one object matching the query parameter (in Java)? I'm very new to using Firebase and as far as I can understand the documentation, orderBy() is an asynchronous method that lasts indefinitely, which is why I'm having trouble figuring out how to perform operations on data after the query. Is there some callback notation that allows me to quickly retrieve and return a value and end the query, or am I missing something in the Firebase documentation?
For example, in this method, I want to just figure out if the database contains a specified user and return true if the query matches the user.
public void containsUser(String user) {
DatabaseReference ref = getDatabaseRef("users");
ref.orderByKey().equalTo("user1").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
System.out.println(dataSnapshot.getKey());
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s){}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {}
#Override
public void onCancelled(DatabaseError databaseError) {
System.out.println(databaseError.getMessage());
}
});
}
Is there an easy way to do this?
To get a single value use addListenerForSingleValueEvent
DatabaseReference ref = getDatabaseRef("users");
ref.child("user1").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String value = (String) dataSnapshot.getValue();
// do your stuff here with value
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
There is a users subtree in my Firebase Database which keeps basic user info and following/followers for that user, this subtree is structured like this:
"users": {
"userId-1": {
"userName": "Namey McNameface",
"following": {
"followingId-1": true,
"followingId-2": true,
.
.
},
"followers": {
"followerId-1": true,
"followerId-2": true,
.
.
}
},
"userId-2": {},
"userId-3": {},
.
.
}
I want to send a notification to user's phone whenever someone starts to following him/her.
In my own server, I listen to the followers subtree. When a child added, user will see a notification.
The problem is that, at some point I will need to deploy the next version of my server and when I do that OnChildAdded event will fire for all children (followers) and all users will see wrong notifications about their followers starting to follow them.
I can store the sent-notification info in my database to solve this problem, but this will be a time-consuming job for me.
Another way is that I can skip the initial firing of OnChildAdded event, if that is possible in Firebase Database.
Thank you in advance.
Edit:
I am using Java and Spring for my server.
This is my FirebaseService. I am creating a UserListener and starting to listen all users.
#Service
public class FirebaseService {
public FirebaseService() {
UserListener userListener = new UserListener();
FirebaseDatabase.getInstance().getReference(Tag.USERS).addChildEventListener(userListener);
}
}
This is UserListener. It creates a FollowerListener to listen followers subtree for each user.
public class UserListener implements ChildEventListener {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
String userId = dataSnapshot.getKey();
FollowerListener followerListener = new FollowerListener(userId);
FirebaseDatabase.getInstance().getReference(Tag.USERS).child(userId).child(Tag.FOLLOWERS).addChildEventListener(followerListener);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
}
This is FollowerListener. When a user ID is added to followers, FollowerListener sends notification. It basically gets the followed user's FirebaseInstanceId and follower user's screen name. Then sends notification.
public class FollowerListener implements ChildEventListener {
private final String followedUserId;
public FollowerListener(String followedUserId){
this.followedUserId = followedUserId;
}
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
String followerUserId = dataSnapshot.getKey();
FirebaseDatabase.getInstance().getReference(Tag.USERS).child(followedUserId)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User followedUser = dataSnapshot.getValue(User.class);
if(followedUser != null){
FirebaseDatabase.getInstance().getReference(Tag.USERS).child(followerUserId)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User followerUser = dataSnapshot.getValue(User.class);
if(followerUser != null){
// Send notification to followed user.
NotificationInfo notificationInfo = new NotificationInfo();
notificationInfo.setTitle("test title");
notificationInfo.setText(followerUser.getScreenName() + " seni takip etmeye başladı.");
notificationInfo.setClickAction(Tag.ACTION_GO_TO_PROFILE);
NotificationData data = new NotificationData();
data.setUserId(followerUserId);
NotificationRequestBean bean = new NotificationRequestBean();
bean.setTo(followedUser.getFirebaseInstanceId());
bean.setBody("test body");
bean.setTitle("test title");
bean.setPriority("normal");
bean.setDelayWhileIdle(false);
bean.setNotification(notificationInfo);
bean.setData(data);
AppUtils.sendNotification(bean);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
}
The problem is whenever I deploy the server, FollowerListener attaches to all users's followers subtree. Then OnChildAdded event is firing for each follower initially. It should not send notifications every time I deploy the server.
Thanks.
I've found a very simple solution for this problem. I just added a boolean field in the followers subtree called processedByServer. So it became like this:
"followers": {
"followerId-1": {
"processedByServer": true // already processed.
},
"followerId-2": {
"processedByServer": false // not processed yet, but will be.
},
.
.
}
This processedByServer is initially false, after I made my necessary processing in the server (send notification etc.) I change this value to true. The server only needs to process when this field is false. So after I redeploy/reboot my server, it won't send wrong notifications anymore.
But there is also downsides of this solution:
I added an extra field just for that, but I think it's ok.
When the server restarts, it has to check all of the processedByServer fields in the followers subtree, which may be a time consuming job for the server.