Read and write values in Firebase Database problem - android

I want to read 2 values from Firebase (if exist as in first user there are not) and if needed to update them. Actually i m trying at first to do it with one value, with no luck. My code is
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user != null) {
final String uid = Objects.requireNonNull( mAuth.getCurrentUser() ).getUid();
mScoreReference.child( uid ).addListenerForSingleValueEvent( new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
User oldscore = dataSnapshot.getValue( User.class );
if (dataSnapshot.exists()) {
if(oldscore.getScore()==null){
oldscore.setScore(String.valueOf( 0));
String oldscorevalue = Objects.requireNonNull(oldscore).getScore();
int convertedscore = Integer.parseInt(oldscorevalue);
if (convertedscore > 0) {
//Upload points to Database
mScoreReference.child(uid).child( "Score" )
.setValue( convertedscore + newScore );
} else mScoreReference.child(uid).child( "Score" ).setValue( newScore );
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
} );
I managed to convert null(the first value as score is not exist) and to set score. So my problem is why i cant update the value, and how can i update 2 values at the same time? Probably i have to use Transaction, but i m not familiar at all with this. I m reading, but i cant find how to convert this code to Transaction.
UPDATE
I tried with Transaction.
mScoreReference.child( uid ).runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
User user = mutableData.getValue(User.class);
if (user.getScore() == null) {
mScoreReference.child(uid).child( "Score" ).setValue( newScore );
}
else{
String oldscorevalue = Objects.requireNonNull(user).getScore();
int convertedscore = Integer.parseInt(oldscorevalue);
mScoreReference.child(uid).child( "Score" )
.setValue( convertedscore + newScore );
}
// Set value and report transaction success
mutableData.setValue(user);
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
// Transaction completed
Log.d(TAG, "postTransaction:onComplete:" + databaseError);
}
});
and i have strange results. At first time do nothing, then adds Score and another irelavent User value, and after this adds 2 Score fields in Database.

Related

Increment values in firebase realtime

There can be mutiple people on the app click a button that will Increment value in firebase and get that value back, but if each person clicks the button at the same time right now, they will get the same value, how do I Increment with each person getting a differnt value?
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("queue");
reference.child(queue_id).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
currentNum = (long) dataSnapshot.child("currentNumber").getValue();
++currentNum;
dataSnapshot.child("currentNumber").getRef().setValue(++currentNum);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
To prevent conflicts from multiple users updating the same node at (almost) the same time, you need to use a transaction. In your case that'd look something like:
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("queue");
DatabaseReference numberRef = reference.child(queue_id).child("currentNumber");
numberRef.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
Long value = mutableData.getValue(Long.class);
if (value == null) {
mutableData.setValue(1);
}
else {
mutableData.setValue(value+1);
}
return Transaction.success(mutableData);
}
}
#Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
// Transaction completed
Log.d(TAG, "runTransaction:onComplete:" + databaseError);
}
As per your code, you're incrementing the counter by 1 but you are not posting the same in the database. Use HashMap to update the data in your database when you modify it.
You can try the below code:
button.setOnClickListerner(new View.onClickListener() {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("queue");
reference.child(queue_id).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
currentNum = (long) dataSnapshot.child("currentNumber").getValue();
long newValue = currentNum + 1;
HasMap<String, Object> hashMap = new HashMap<>();
hashMap.put ("currentNumber", newValue);
reference.child(queue_id).updateChildren(hashMap);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
};
Hope this works!
You can use method transactions. Refer to this link: https://firebase.google.com/docs/database/android/read-and-write#save_data_as_transactions"
private void onStarClicked(DatabaseReference postRef) {
postRef.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
Post p = mutableData.getValue(Post.class);
if (p == null) {
return Transaction.success(mutableData);
}
if (p.stars.containsKey(getUid())) {
// Unstar the post and remove self from stars
p.starCount = p.starCount - 1;
p.stars.remove(getUid());
} else {
// Star the post and add self to stars
p.starCount = p.starCount + 1;
p.stars.put(getUid(), true);
}
// Set value and report transaction success
mutableData.setValue(p);
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
// Transaction completed
Log.d(TAG, "postTransaction:onComplete:" + databaseError);
}
});
}

firebase read and write - in one event

i have a real time firebase database ...
1 - i need to create a child when there is no child exist in database in the name "book"
book structure:
"book":[{slno:1,
"name":"harry potter",
"author":"J. K. Rowling"
}]
2- if database have the child named "book" then i need to create new child for "book"
but this time i want the "slno" , last added child's "slno + 1"
{ slno:"//last added child's slno + 1",
"name":"new book",
"author":"sample author"
}
tell me a best way to write a code on above situation ...
i am new to firebase...
[platform : Android]
i already tried this[and it's working]
but i don't know its good solution when happen database write operation
from multiple divices at same time [i want , never repeat slno]
... any one have better solution
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
rootRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.hasChild("book")) {
// run some code
secondtime();
}else {
final DatabaseReference databaseP2 = FirebaseDatabase.getInstance().getReference();
BookModelClass bookModel = new BookModelClass(1,"harry potter","J. K. Rowling");
databaseP2.child("book").push().setValue(bookModel);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
private void secondtime(){
final DatabaseReference databaseP2 = FirebaseDatabase.getInstance().getReference();
Query lastQuery = databaseP2.child("book").orderByKey().limitToLast(1);
lastQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
int slno = 0 ;
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
slno =postSnapshot.child("slno").getValue(Integer.class);
}
final DatabaseReference databaseP2 = FirebaseDatabase.getInstance().getReference("book");
BookModelClass bookModel = new BookModelClass(slno+1,"new book","sample author");
databaseP2.push().setValue(bookModel);
}
#Override
public void onCancelled(DatabaseError databaseError) {
//Handle possible errors.
}
});
}
Since the new value of the node depends on the existing value (or its existence at all), you'll want to use a Firebase Database transaction for this operation.
This means you'll need to read the entire book node, but your current solution also does that, so it's the same in read performance. It'll be something along these lines:
rootRef.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
if (mutableData.hasChildren()) {
int slno = 0 ;
for (MutableData postData: mutableData.getChildren()) {
slno = postData.child("slno").getValue(Integer.class);
}
BookModelClass bookModel = new BookModelClass(slno+1,"new book","sample author");
String newKey = rootRef.push().getKey();
// Set value and report transaction success
mutableData.child(newKey).setValue(bookModel);
}
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
// Transaction completed
Log.d(TAG, "bookTransaction:onComplete:" + databaseError);
}
});

Retrieving data realtime from firebase that have 2 childs for 2 apps in 1 firebase [Android]

I create 2 android apps, for the user and for admin. The user app can crud the "pelanggaran", the admin app can see all the pelanggaran realtime.
Here, I am stuck in retrieving the data in real time for the admin app. Here's the database hierarchy.
this is the references: pelanggaran and user
each user has pelanggaran. so, the first child of pelanggaran is userID, then the pelanggaranID, then the values
I tried googling, I got it, but it is not real-time. Here's my code
private void refreshList(){
databaseUser = FirebaseDatabase.getInstance().getReference("User");
pelanggaranList = new ArrayList<>();
databaseUser.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
pelanggaranList.clear();
long numberUsers = dataSnapshot.getChildrenCount();
Log.w("total user", "" + numberUsers);
for (DataSnapshot dsUser : dataSnapshot.getChildren()){
User user = dsUser.getValue(User.class);
final String userId = user.getUserId();
Log.w("id user", userId);
databasePelanggaran = FirebaseDatabase.getInstance().getReference("Pelanggaran").child(userId);
databasePelanggaran.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot1) {
for (DataSnapshot dsPelanggaran : dataSnapshot1.getChildren()){
Pelanggaran pelanggaran = dsPelanggaran.getValue(Pelanggaran.class);
Log.w("id pelanggaran", userId + " : " + pelanggaran.getIdPelanggaran());
pelanggaranList.add(pelanggaran);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
PelanggaranAdapter pelanggaranAdapter = new PelanggaranAdapter(MainActivity.this, pelanggaranList);
recyclerView.setAdapter(pelanggaranAdapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
It can take all the values from each user but it is not real time. And sometimes the data doesn't appears. What should I do to make it realtime?

Adding default values firebase

I am using Firebase-UI in my android project with email/password provider. The structure of the database is:
profiles
user-id
age
height
user-id
On signup I want to enter a new node in profiles with default values. The user can edit and save the values later. For an existing user I just want to read the values and display them in the UI. I have tried using ChildEventListener and dataSnapshot.hasChild(uid) to detect if the user already exists but it isn't working. This is the AuthStateListener:
mAuthStateListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = mFirebaseAuth.getCurrentUser();
if(user != null)
{
onSignedInInitialize(user);
}
else
{
onSignedOutCleanup();
startActivityForResult(AuthUI.getInstance().createSignInIntentBuilder().setProviders(providers).build(), SIGN_IN);
}
}
};
This is what I have tried:
private void onSignedInInitialize(final FirebaseUser user) {
userRef = mProfilesDBReference.child(user.getUid());
if(mValueEventListener == null)
{
mValueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.getValue() == null)
{
UserProfile profile = new UserProfile("username", null, 100, 100);
userRef.setValue(profile);
UpdateUI(profile);
}
else
{
UserProfile profile = dataSnapshot.getValue(UserProfile.class);
UpdateUI(profile);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
}
userRef.addValueEventListener(mValueEventListener);
}
Lets say the userid is stored in uid;
You can fetch the height and age like this.
DatabasReference userRef = database.getReference("profiles").child(uid);
userRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.getValue() == null){
//User data doesnt exist
}
else
{
HashMap<String,String> userMap =
(HashMap<String,String>)dataSnapshot.getValue();
String age = userMap.get("age");
String height = userMap.get("height");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Note that i have assumed that you have stored them as Strings , firebase usually converts ints to longs. So if you used ints , convert the value type of hashmap as Long.
A much better way would be to create a POJO class.

How do I rework these Firebase ValueEventListeners to be readable?

If there's something I'm struggling with it's how to write good looking code with asynchronous calls to Firebase. Below is the code for one of my functions that retrieves all users and the current user from Firebase and checks the distance between their chosen locations.
I can't really move each of the ValueEventListeners to separate functions since they all have to fire in succession.
How do I make this code look good?
private void getUsersToDisplay() {
// Get users to display from firebase
Log.w(TAG, "User fetch initialized");
DatabaseReference currentUserRef = mDatabase.getReference().child("users/" + mCurrentUser.getUid());
// Get current user from firebase
currentUserRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot currentUserSnapshot) {
// Get the users chosen location for use in distance calculation
final Double currentUserChosenLatitude =
Double.parseDouble(currentUserSnapshot.child("chosen_location/latlng/latitude").getValue().toString());
final Double currentUserChosenLongitude =
Double.parseDouble(currentUserSnapshot.child("chosen_location/latlng/longitude").getValue().toString());
DatabaseReference usersRef = mDatabase.getReference().child("users");
// Get all users from firebase
usersRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot allUsersSnapshot) {
// For all users in firebase
for (DataSnapshot userSnap : allUsersSnapshot.getChildren()) {
String userId = userSnap.getKey();
DatabaseReference userRef = mDatabase.getReference().child("users/" + userId);
// If the user isn't the current user
if (!userId.equals(mCurrentUser.getUid())) {
// Get the user's info from firebase
userRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot userSnapshot) {
Double userChosenLatitude =
Double.parseDouble(userSnapshot.child("chosen_location/latlng/latitude").getValue().toString());
Double userChosenLongitude =
Double.parseDouble(userSnapshot.child("chosen_location/latlng/longitude").getValue().toString());
float[] results = new float[1];
Location.distanceBetween(
currentUserChosenLatitude,
currentUserChosenLongitude,
userChosenLatitude,
userChosenLongitude,
results);
// If the user is within 10km of the current user, display it
if (results[0] < 10000) {
users.put(userSnapshot.getKey(), (String) userSnapshot.child("first_name").getValue());
mUserAdapter.add((String) userSnapshot.child("first_name").getValue());
Log.w(TAG, "User to display fetch complete");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "User to display fetch cancelled");
}
});
}
}
Log.w(TAG, "Users fetch completed");
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "Users fetch cancelled");
}
});
Log.w(TAG, "Current user fetch complete");
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "Current user fetch cancelled");
}
});
}
I write my code like this so it easier to read:
onCreate() {
dataRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
doSomething(dataSnapshot);
...
}
...
}
}
doSomething(DataSnapshot dataSnapshot) {
...
}
If I want a Firebase call to run after another Firebase call, I place it inside doSomething.
BUT, if that call doesn't have to run after each other (like "get current user" and "get all user" call from your sample code), I made it like this:
boolean firstCallDone = false;
boolean secondCallDone = false;
DataSnapshot firstDataSnapshot = null;
DataSnapshot secondDataSnapshot = null;
onCreate() {
firstRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
firstCallDone = true;
firstDataSnapshot = dataSnapshot;
if (firsCallDone && secondCallDone)
doSomething();
}
...
}
secondRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
secondCallDone = true;
secondDataSnapshot = dataSnapshot;
if (firsCallDone && secondCallDone)
doSomething();
}
...
}
}
doSomething() {
// do something with firstDataSnapshot and secondDataSnapshot
...
}
Hope this help.

Categories

Resources