Firebase Admin database can't read [duplicate] - java

I'm trying to update parts of a WebView in my Android app with data I'm getting from a peer connected via Firebase. For that, it could be helpful to execute blocking operations that will return the needed data. For example, an implementation of the Chat example that will wait until another chat participant writes something before the push.setValue() to return.
Is such a behavior possible with Firebase?

import com.google.android.gms.tasks.Tasks;
Tasks.await(taskFromFirebase);

On a regular JVM, you'd do this with regular Java synchronization primitives.
For example:
// create a java.util.concurrent.Semaphore with 0 initial permits
final Semaphore semaphore = new Semaphore(0);
// attach a value listener to a Firebase reference
ref.addValueEventListener(new ValueEventListener() {
// onDataChange will execute when the current value loaded and whenever it changes
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// TODO: do whatever you need to do with the dataSnapshot
// tell the caller that we're done
semaphore.release();
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
// wait until the onDataChange callback has released the semaphore
semaphore.acquire();
// send our response message
ref.push().setValue("Oh really? Here is what I think of that");
But this won't work on Android. And that's a Good Thing, because it is a bad idea to use this type of blocking approach in anything that affects the user interface. The only reason I had this code lying around is because I needed in a unit test.
In real user-facing code, you should go for an event driven approach. So instead of "wait for the data to come and and then send my message", I would "when the data comes in, send my message":
// attach a value listener to a Firebase reference
ref.addValueEventListener(new ValueEventListener() {
// onDataChange will execute when the current value loaded and whenever it changes
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// TODO: do whatever you need to do with the dataSnapshot
// send our response message
ref.push().setValue("Oh really? Here is what I think of that!");
}
#Override
public void onCancelled(FirebaseError firebaseError) {
throw firebaseError.toException();
}
});
The net result is exactly the same, but this code doesn't required synchronization and doesn't block on Android.

I came up with another way of fetching data synchronously.
Prerequisite is to be not on the UI Thread.
final TaskCompletionSource<List<Objects>> tcs = new TaskCompletionSource<>();
firebaseDatabase.getReference().child("objects").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Mapper<DataSnapshot, List<Object>> mapper = new SnapshotToObjects();
tcs.setResult(mapper.map(dataSnapshot));
}
#Override
public void onCancelled(DatabaseError databaseError) {
tcs.setException(databaseError.toException());
}
});
Task<List<Object>> t = tcs.getTask();
try {
Tasks.await(t);
} catch (ExecutionException | InterruptedException e) {
t = Tasks.forException(e);
}
if(t.isSuccessful()) {
List<Object> result = t.getResult();
}
I tested my solution and it is working fine, but please prove me wrong!

Here's a longer example based on Alex's compact answer:
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.firestore.CollectionReference;
import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.Query;
import com.google.firebase.firestore.QuerySnapshot;
final FirebaseFirestore firestore = FirebaseFirestore.getInstance();
final CollectionReference chatMessageReference = firestore.collection("chatmessages");
final Query johnMessagesQuery = chatMessageReference.whereEqualTo("name", "john");
final QuerySnapshot querySnapshot = Tasks.await(johnMessagesQuery.get());
final List<DocumentSnapshot> johnMessagesDocs = querySnapshot.getDocuments();
final ChatMessage firstChatMessage = johnMessagesDocs.get(0).toObject(ChatMessage.class);
Note that this is not good practice as it blocks the UI thread, one should use a callback instead in general. But in this particular case this helps.

If anyone is also thinking about how to use Kotlin's coroutine you can use kotlinx-coroutines-play-services.
Add to your app build.gradle file:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.1.1"
Then simply:
suspend fun signIn(email: String, password: String) {
try {
val auth: FirebaseAuth = FirebaseAuth.getInstance()
auth.signInWithEmailAndPassword(email, password).await()
} catch (e: FirebaseAuthException) {
println("${e.errorCode}: ${e.message}")
}
}

I made a simple class to call tasks synchronously in Android.
Note that this is similar to Javascript's async await function.
Check my gist.
Here's a sample code to use it.
TasksManager.call(() -> {
Tasks.await(AuthManager.signInAnonymously());
// You can use multiple Tasks.await method here.
// Tasks.await(getUserTask());
// Tasks.await(getProfileTask());
// Tasks.await(moreAwesomeTask());
// ...
startMainActivity();
return null;
}).addOnFailureListener(e -> {
Log.w(TAG, "signInAnonymously:ERROR", e);
});

Related

Firebase Realtime Database Listener in Java Application doesn't work(Not Android) [duplicate]

I'm trying to update parts of a WebView in my Android app with data I'm getting from a peer connected via Firebase. For that, it could be helpful to execute blocking operations that will return the needed data. For example, an implementation of the Chat example that will wait until another chat participant writes something before the push.setValue() to return.
Is such a behavior possible with Firebase?
import com.google.android.gms.tasks.Tasks;
Tasks.await(taskFromFirebase);
On a regular JVM, you'd do this with regular Java synchronization primitives.
For example:
// create a java.util.concurrent.Semaphore with 0 initial permits
final Semaphore semaphore = new Semaphore(0);
// attach a value listener to a Firebase reference
ref.addValueEventListener(new ValueEventListener() {
// onDataChange will execute when the current value loaded and whenever it changes
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// TODO: do whatever you need to do with the dataSnapshot
// tell the caller that we're done
semaphore.release();
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
// wait until the onDataChange callback has released the semaphore
semaphore.acquire();
// send our response message
ref.push().setValue("Oh really? Here is what I think of that");
But this won't work on Android. And that's a Good Thing, because it is a bad idea to use this type of blocking approach in anything that affects the user interface. The only reason I had this code lying around is because I needed in a unit test.
In real user-facing code, you should go for an event driven approach. So instead of "wait for the data to come and and then send my message", I would "when the data comes in, send my message":
// attach a value listener to a Firebase reference
ref.addValueEventListener(new ValueEventListener() {
// onDataChange will execute when the current value loaded and whenever it changes
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// TODO: do whatever you need to do with the dataSnapshot
// send our response message
ref.push().setValue("Oh really? Here is what I think of that!");
}
#Override
public void onCancelled(FirebaseError firebaseError) {
throw firebaseError.toException();
}
});
The net result is exactly the same, but this code doesn't required synchronization and doesn't block on Android.
I came up with another way of fetching data synchronously.
Prerequisite is to be not on the UI Thread.
final TaskCompletionSource<List<Objects>> tcs = new TaskCompletionSource<>();
firebaseDatabase.getReference().child("objects").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Mapper<DataSnapshot, List<Object>> mapper = new SnapshotToObjects();
tcs.setResult(mapper.map(dataSnapshot));
}
#Override
public void onCancelled(DatabaseError databaseError) {
tcs.setException(databaseError.toException());
}
});
Task<List<Object>> t = tcs.getTask();
try {
Tasks.await(t);
} catch (ExecutionException | InterruptedException e) {
t = Tasks.forException(e);
}
if(t.isSuccessful()) {
List<Object> result = t.getResult();
}
I tested my solution and it is working fine, but please prove me wrong!
Here's a longer example based on Alex's compact answer:
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.firestore.CollectionReference;
import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.Query;
import com.google.firebase.firestore.QuerySnapshot;
final FirebaseFirestore firestore = FirebaseFirestore.getInstance();
final CollectionReference chatMessageReference = firestore.collection("chatmessages");
final Query johnMessagesQuery = chatMessageReference.whereEqualTo("name", "john");
final QuerySnapshot querySnapshot = Tasks.await(johnMessagesQuery.get());
final List<DocumentSnapshot> johnMessagesDocs = querySnapshot.getDocuments();
final ChatMessage firstChatMessage = johnMessagesDocs.get(0).toObject(ChatMessage.class);
Note that this is not good practice as it blocks the UI thread, one should use a callback instead in general. But in this particular case this helps.
If anyone is also thinking about how to use Kotlin's coroutine you can use kotlinx-coroutines-play-services.
Add to your app build.gradle file:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.1.1"
Then simply:
suspend fun signIn(email: String, password: String) {
try {
val auth: FirebaseAuth = FirebaseAuth.getInstance()
auth.signInWithEmailAndPassword(email, password).await()
} catch (e: FirebaseAuthException) {
println("${e.errorCode}: ${e.message}")
}
}
I made a simple class to call tasks synchronously in Android.
Note that this is similar to Javascript's async await function.
Check my gist.
Here's a sample code to use it.
TasksManager.call(() -> {
Tasks.await(AuthManager.signInAnonymously());
// You can use multiple Tasks.await method here.
// Tasks.await(getUserTask());
// Tasks.await(getProfileTask());
// Tasks.await(moreAwesomeTask());
// ...
startMainActivity();
return null;
}).addOnFailureListener(e -> {
Log.w(TAG, "signInAnonymously:ERROR", e);
});

Firestore asynchronous API getting null values outside readData

this is my logcat output
I am trying to fetch data from Firestore and work on them tried this but not working
getting null value for "emergencyNumber" outside "readData()"
I have tried this solution Text but still getting null
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_emergency_contact);
Log.d("TAG","Initial");
firebaseAuth = FirebaseAuth.getInstance();
fStore = FirebaseFirestore.getInstance();
userID = firebaseAuth.getCurrentUser().getUid();
readData(new FirebaseCallBack() {
#Override
public void onCallback(String str) {
emergencyNumber = str;
//Toast.makeText(EmergencyContact.this, emergencyNumber, Toast.LENGTH_SHORT).show();
phone = (TextView) dialog.findViewById(R.id.EmergencyContactNumber) ;
//emergencyNumber = phone.getText().toString() + "Hello";
phone.setText(emergencyNumber);
Log.d("TAG",emergencyNumber+"inner read me");
}
});
Log.d("TAG",emergencyNumber+"middle");
Log.d("TAG",emergencyNumber+"end");
}
private void readData(FirebaseCallBack firebaseCallBack) {
documentReference.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()){
DocumentSnapshot document = task.getResult();
long l = document.getLong("EmergencyContact");
emergencyNumber = "0" + Long.toString(l);
firebaseCallBack.onCallback(emergencyNumber);
//Toast.makeText(EmergencyContact.this, emergencyNumber, Toast.LENGTH_SHORT).show();
Log.d("TAG",emergencyNumber+"exit read me");
}
}
});
}
private interface FirebaseCallBack {
void onCallback(String str);
}
}
Firestore asynchronous API getting null values outside readData.
Any code that needs data from Cloud Firestore needs to be inside the "onComplete()" method, or be called from there. It doesn't really matter if you create another callback, the same rules apply. This means that you cannot use the value of "emergencyNumber" outside the "onCallback()" method. Please note that this method fires, only when "onComplete()" method fires, hence that behavior. When the following Log statement is triggered:
Log.d("TAG",emergencyNumber+"middle");
The data isn't finished loading yet, that's why you have that order of execution in your logcat.
If you are not comfortable with callbacks, then I recommend you using the modern way of dealing with asynchronous programming when getting data from Firestore, which is using LiveData and ViewModel. Here you can find an example from one of my repositories where I have used the MVVM architecture pattern with LiveData and ViewModel to authenticate users in Firebase.
If you consider at some point in time to try using Kotlin, please check below an example:
https://github.com/alexmamo/FirestoreJetpackCompose
Where I have used Kotlin Coroutine for getting data from Firestore.

Firebase + RxJava, onComplete() event [duplicate]

This question already has answers here:
Combining Firebase realtime data listener with RxJava
(5 answers)
Closed 2 years ago.
Does anyone knows how to connect Firebase with RxJava so when I load ALL my data from database then it runs arrayAdapter.notifyDataSetChanged() ??
I was thinking to write it in onComplete() method but it still runs before loading all data
Completable.fromCallable(new Callable<List<cards>>() {
#Override
public List<cards> call() throws Exception {
newUserDb.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.child(currentUID).child("sex").exists()) {
myInfo.put("sex", dataSnapshot.child(currentUID).child("sex").getValue().toString());
}
if (dataSnapshot.child(currentUID).child("dateOfBirth").exists()) {
int myAge = stringDateToAge(dataSnapshot.child(currentUID).child("dateOfBirth").getValue().toString());
myInfo.put("age", String.valueOf(myAge));
}
if (dataSnapshot.child(currentUID).child("connections").child("yes").exists()) {
for (DataSnapshot ds : dataSnapshot.child(currentUID).child("connections").child("yes").getChildren()) {
if (!dataSnapshot.child(currentUID).child("connections").child("matches").hasChild(ds.getKey())) {
Log.d("rxJava", "onDataChange: " + ds.getKey());
first.add(ds.getKey());
getTagsPreferencesUsers(dataSnapshot.child(ds.getKey()), true);
}
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
return rowItems;
}
}).subscribeOn(Schedulers.computation())
.subscribe(new CompletableObserver() {
#Override
public void onSubscribe(Disposable d) {
Log.d("rxJava", "Test RxJAVA, onSubscribe");
}
#Override
public void onComplete() {
Log.d("rxJava", "Test RxJAVA, onComplete");
}
#Override
public void onError(Throwable error) {
Log.d("rxJava", "Test RxJAVA, onError");
}
});
and the output is
2020-06-04 23:36:28.797 29515-29515/com.example.tinderapp D/rxJava: Test RxJAVA, onSubscribe
2020-06-04 23:36:28.800 29515-29612/com.example.tinderapp D/rxJava: Test RxJAVA, onComplete
2020-06-04 23:36:29.018 29515-29515/com.example.tinderapp D/rxJava: onDataChange: a4hqGgAJBRTVJOlPp3blNDt5v7q1
2020-06-04 23:36:29.022 29515-29515/com.example.tinderapp D/rxJava: onDataChange: aA9HAOtaB7ao6vzKqqBNp0iaBev2
I would say, this is expected behavior, which is described in the following:
Completable.fromCallable
fromCallable takes a Lambda, which returns a List on subscription. In your case, a database-connection is opened as-well, which is basically fall through, because the callback is registered via callback non-blocking.
subscribeOn
this makes sure, that the subscribeAcutal from fromCallable is called from given scheduler. Therefore the subscribing thread and and emitting thread are decoupled.
You get onComplete first, because the fromCallable will return rowItems immediately and the database connection will stay open, because you did not remove the listener. After a while you get data-base callback logs, because the database connection is still open and the listener is still registered.
You want to actually do something like this:
Single.create<List<Card>> { emitter ->
// register onChange callback to database
// callback will be called, when a value is available
// the Single will stay open, until emitter#onSuccess is called with a collected list.
newUserDb.addListenerForSingleValueEvent {
// do some stuff
emitter.onSuccess(listOf()) // return collected data from database here...
}
emitter.setCancellable {
// unregister addListenerForSingleValueEvent from newUserDb here
}
}.subscribeOn(Schedulers.computation())
.subscribe(
// stuff
)
If you want to have a constant stream of updates, exchange Single with Observable/ Flowable

How to get data from listener?

I am cross-platforming my app using Android and iOS and Firebase. In the iOS app, I have a option for the driver to use bids for the requested ride, whereas in the android app, it is just a calculated price.
If the Android rider requests a ride with an iOS driver, the iOS driver can offer a discounted bid rather than the calculated ridePrice.
In Firebase, in the RideRequests node, it adds ridePrice as the price to be paid but if its a bid price, it also adds a bidPrice. I am trying to set it up that if 'rideBidPrice' exists, then rideAmt = rideBidPrice but if not, then rideAmt = ridePrice.
Unfortunately, what I am doing isn't getting to the PayPalPayment method.
How can I do this?
firebase db:
"RideRequests" : {
"buIpWNEFgmZJWN3jIB9IwS8r74d2" : { // rider Id
"archiveTimestamp" : "2019-03-17 22:00",
"currentAddress" : "2 Peel Plaza",
"destAddress" : "Courtenay Bay Causeway",
"destLat" : 47.277879500000004,
"destLong" : -63.04432369999999,
"driver" : "Dean Winchester",
"driverID" : "nD1v8oxHv3ObdQAKeKjuTt6f5TL2",
"rideBidPrice" : 5.74,
"ridePrice" : 8.38,
"riderPaid" : "false",
"status" : "driverBid",
"userId" : "buIpWNEFgmZJWN3jIB9IwS8r74d2",
"username" : "riderANDROID"
}
Code:
private void checkForBids() {
Log.e(TAG, "checkForBids");
DatabaseReference bids = FirebaseDatabase.getInstance().getReference(Common.request_tbl)
.child(riderId).child("rideBidPrice");
bids.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
bidPrice = dataSnapshot.getValue(Double.class);
if (bidPrice != null) {
rideAmt = bidPrice;
} else {
rideAmt = ridePrice;
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
private void payPalPayment() {
Log.e(TAG, "payPalPayment");
PayPalPayment payment = new PayPalPayment(new BigDecimal(rideAmt),
"CAD", "Ryyde Payment ", PayPalPayment.PAYMENT_INTENT_SALE);
// PaymentActivity is created by PayPal API
Intent intent = new Intent(this, PaymentActivity.class);
intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config);
intent.putExtra(PaymentActivity.EXTRA_PAYMENT, payment);
startActivityForResult(intent, PAYPAL_REQUEST_CODE);
}
I also tried putting the payPalPayment method inside the block but won't work because of the intent.
EDIT #1
This works:
As per #AlexMamo post to below post:
How to return DataSnapshot value as a result of a method?
I have added an interface:
public interface MyCallback {
void onCallback(Double value);
}
The method that is actually getting the data from the database:
public void readData(final MyCallback myCallback) {
Log.e(TAG, "readData");
DatabaseReference bids = FirebaseDatabase.getInstance().getReference(Common.request_tbl)
.child(riderId).child("rideBidPrice");
bids.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Double value = dataSnapshot.getValue(Double.class);
myCallback.onCallback(value);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Then I call the method inside the PayPalPayment method like this:
private void payPalPayment() {
readData(new MyCallback() {
#Override
public void onCallback(Double value) {
PayPalPayment payment = new PayPalPayment(new BigDecimal(value),
"CAD", "Ryyde Payment ", PayPalPayment.PAYMENT_INTENT_SALE);
// PaymentActivity is created by PayPal API
Intent intent = new Intent(RiderHome.this, PaymentActivity.class);
intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config);
intent.putExtra(PaymentActivity.EXTRA_PAYMENT, payment);
startActivityForResult(intent, PAYPAL_REQUEST_CODE);
}
});
}
You cannot create your rideAmt as a global variable and simply use it inside your payPalPayment() method because the Firebase API is asynchronous, meaning that onDataChange() method returns immediately after it's invoked, and the callback from the Task it returns, will be called some time later. There are no guarantees about how long it will take. So it may take from a few hundred milliseconds to a few seconds before that data is available. Because that method returns immediately, the value of your rideAmt variable you're trying to use it outside the onDataChange() method, will not have been populated from the callback yet.
Basically, you're trying to use the value of rideAmt synchronously from an API that's asynchronous. That's not a good idea. You should handle the API asynchronously as intended.
A quick solve for this problem would be to move all the logic that exist in your payPalPayment() method right inside the onDataChange() method. In this way, the data that you are trying to get will be available at that time.
If you need to use it outside the callback, I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.

Promises in Java

The issue I am ultimately trying to solve, before I pose my question, is the synchronicity of querying my Firebase database and writing code based on the result. A simple example to illustrate:
Boolean userFound = false;
DatabaseReference userName = FirebaseDatabase.getInstance().getReference().child("Profiles").child("Name");
userName.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String name = dataSnapshot.getValue().toString();
userFound = true;
Toast.makeText(getBaseContext(), "Welcome back, " + name + ".", Toast.LENGTH_SHORT).show();
}
#Override
public void onCancelled(DatabaseError databaseError) {
//Never used this section
}
});
If (!userFound) {
Toast.makeText(getBaseContext(), "User not found.", Toast.LENGTH_SHORT).show();
}
In the example above, the listener looks for a name in the database. If the name is found it gives a welcome message and sets "userFound" to true. If a name is not found, "userFound" will remain as false and you can generate a user not found message.
The problem with this is that everything runs at the same instant and so you will always get the "User not found" message instantly, and then a few seconds later the listener might actually find the user and say "Welcome back".
I have been looking into how I can possible resolve this, and I have found Java Promises. Am I looking in the right direction? Here are two promise examples:
CompletableFuture.supplyAsync(this::failingMsg)
.exceptionally(ex -> new Result(Status.FAILED))
.thenAccept(this::notify);
This code looks great, and the article here is very detailed in its usage: http://www.deadcoderising.com/java8-writing-asynchronous-code-with-completablefuture/
Except for the fact that is will ONLY work in API 24 and above. Which means your app will not work on 90% of devices. So this is essentially worthless.
The other way of doing this is as follows:
try {
Promise { client.newCall(request).execute() }
.then { ... }
.thenAsync { ... }
.then { ... }
} catch (e: Exception) {
...
}
As explained here: https://medium.com/#sampsonjoliver/promises-in-android-and-java-d6b1c418ea6c
Except that when I try to use this code there is no such thing as Promise. It just says it cannot resolve the symbol. So this guy has written an article on something that doesn't even exist.
Am I looking at the right stuff here? The end game is to make my app wait for the result of any database lookup before continuing to process code. If I cannot do this, then the database becomes completely useless.
Thanks guys. Please help!
The solution with a problem using asynchronous APIs is pretty much always the same: move the code that needs access to the data into the method that is called when the data is available.
So in your case that means moving the check and toast into onDataChange:
DatabaseReference userName = FirebaseDatabase.getInstance().getReference().child("Profiles").child("Name");
userName.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Boolean userFound = false;
String name = dataSnapshot.getValue().toString();
userFound = true;
Toast.makeText(getBaseContext(), "Welcome back, " + name + ".", Toast.LENGTH_SHORT).show();
if (!userFound) {
Toast.makeText(getBaseContext(), "User not found.", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
});
For more on this, see:
Setting Singleton property value in Firebase Listener
Doug's excellent blog post
Querying data from firebase
Wait Firebase async retrive data in android
Handle data returned by an Async task (Firebase)

Categories

Resources