Redo Firebase Database Read on onCancelled - java

I'm writing an Android app and I'm currently trying to read data from my Firebase databese. I followed the documentation here on how to read. Now in the case when OnCancelled is called (meaning the Firebase read has failed) I check if the failure was because of internect connection so I can prompt the user to handle that.
So now my code consists of the following:
A static function in a class that checks for internet, show a dialog to the user, tries to reconnect every two seconds and then shows the dialog again if not connected. It keeps doing that until the internet is connected then it calls back a callback function to do actions after internet connects.
The firebase ValueEventListener with both onDataChange and onCancelled
Here're both codes:
Connection
/**
* Defined in some separate file in a class called SharedStuff
* This function launches a child thread for checking internet connection.
* It also shows a dialog for the user to reconnect
* Body of the function is irrelevant
*
* #param caller The caller activity. Needed to launch dialogs
* #param callback Actions to do when internet connects
* #param timeout A timeout period after which we will stop trying
*/
public static void isInternetAvailable(final Activity caller, final Callable<Void> callback, final int timeout);
Firebase
/**
* Firebase stuff
* Inside my main activity file
*/
mAuth = FirebaseAuth.getInstance();
mDB = FirebaseDatabase.getInstance();
mDB.getReference("users").child(mAuth.getCurrentUser().getUid()).addValueEventListener(new ValueEventListener()
{
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot)
{
// Do stuff
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError)
{
if (databaseError.getCode() == DatabaseError.DISCONNECTED || databaseError.getCode() == DatabaseError.NETWORK_ERROR)
{
/**
* Need to try call the internet connection function here with some callback!
*/
SharedStuff.isInternetAvailable(MainActivity.this, new Callable<Void>()
{
#Override
public Void call()
{
// Need to fill this callback!
return null;
}
}, 2000);
}
else
{
Log.wtf(TAG, "Reading database failed", databaseError.toException());
}
}
});
So now I'm stuck trying to fill this callback with suitable actions. Basically what I'd like it to do is try reading again until the read succeeds, or show the same internet dialog again if the it fails for the same reason (I know it's pretty hard for this to happen, but I have seen some really flickering network connections and would like to be on the safe side by handling this case).
So the methods I've considered/tried are the following:
1- Force Data Changed event to happen
However, I couldn't find any API calls in the firebase documentation that would have this effect.
2- Manually add ValueEventListener again
In this case I use addListenerForSingleValueEvent instead of addValueEventListener but then I try do connect it again inside onCancelled!
The code for this case would look like this:
/**
* Firebase stuff
*/
mAuth = FirebaseAuth.getInstance();
mDB = FirebaseDatabase.getInstance();
mDB.getReference("users").child(mAuth.getCurrentUser().getUid()).addListenerForSingleValueEvent(new ValueEventListener()
{
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot)
{
// Do stuff
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError)
{
if (databaseError.getCode() == DatabaseError.DISCONNECTED || databaseError.getCode() == DatabaseError.NETWORK_ERROR)
{
/**
* Need to call the internet connection function here with some callback!
*/
SharedStuff.isInternetAvailable(WelcomeActivity.this, new Callable<Void>()
{
#Override
public Void call()
{
mDB.getReference("users").child(mAuth.getCurrentUser().getUid()).addListenerForSingleValueEvent(Outer.this);
return null;
}
}, 2000);
}
else
{
Log.wtf(TAG, "Reading database failed", databaseError.toException());
}
}
});
The problem in this case is that the keyword this would be for the Callable object not for the ValueEventListener and I'm not sure what to substitute the word Outer for to make this point to the Listener instead!
What can I do here to reconnect and reread? Whether it's Option 1 or 2 or something else entirely. I just would like to be able to do the following when onCancelled gets called: check internet connection, retry reading when connected, and keep doing this if the connection fails again (You could think of this as recursion)!

onCancelled is triggered:
... in the event that this listener either failed at the server, or is
removed as a result of the security and Firebase Database rules.
It doesn't get invoked if there is a network error. When the client is unable to reach the Firebase server, it will internally retry the connection and any pending reads or writes. You don't have to write any code to handle this situation. In fact, you can't change this behavior.

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);
});

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 can I get data from firebase in android and set it to my model?

I was creating an app and connect it to firebase and I have successfully uploaded the data by firebase documentation, and now I can't get it, I couldn't find any documentation about it.
Can you please show me the code of how to get my data AND set it to my model like for example:
Model.setTitle(firebase.collection.get("Title"));
Get your database and then use a ValueEventListener :)
ValueEventListener postListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get Post object and use the values to update the UI
Post post = dataSnapshot.getValue(Post.class);
HERE GET THE VARIABLE AND SET TO YOUR TITLE :)
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
// ...
}
};
mPostReference.addValueEventListener(postListener);
In some cases you may want a callback to be called once and then immediately removed, such as when initializing a UI element that you don't expect to change. You can use the addListenerForSingleValueEvent() method to simplify this scenario: it triggers once and then does not trigger again.
https://firebase.google.com/docs/database/android/read-and-write

Firebase Admin database can't read [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);
});

How to fetch data on Firebase from time to time?

I am fetching data on Firebase from time to time because I am tracking someone's GPS. That someone is saving his location in an interval of 5 minutes so that I can track his GPS. But my problem is how do I fetch data from Firebase with an interval of 5 minutes too?
Or is there any possible way other than this?
Thanks in advance. :)
So if someone is updating his/her location in every five minutes, then you really don't have to run a CounterDownTimer in your side to track the location in every five minutes. So I think you just need to add a listener to that node at Firebase that you want to track.
So here's a simple implementation for you. Copied from Firebase Tutorial
Firebase ref = new Firebase("https://docs-examples.firebaseio.com/web/saving-data/fireblog/posts");
// Attach an listener to read the data at our posts reference
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
System.out.println(snapshot.getValue());
}
#Override
public void onCancelled(FirebaseError firebaseError) {
System.out.println("The read failed: " + firebaseError.getMessage());
}
});
I'm copying quotes from there too. So that you know it serves your purpose.
This method will be called anytime new data is added to our Firebase
reference, and we don't need to write any extra code to make this
happen.
So each time the person you want to track will update his/her location, you'll get a callback in the method stated above and will take necessary action. You really don't have to implement a polling mechanism to do the tracking. That's the way Firebase works actually.
No Needs to put any kind of service of Scheduler to retrieve data from firebase.
As Firebase provide realtime database .. whenever you push your data on Firebase Database the listener will trigger and you can retrieve your data..
Implement following Listener, using this you can retrieve your data whenever database get update.
DatabaseReference mDatabase =FirebaseDatabase.getInstance().getReference();
ValueEventListener yourModelListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get YOURMODEL object and use the values to update the UI
YourModel mModel = dataSnapshot.getValue(YourModel.class);
Log.e("Data : ",""+mModel);
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
}
};
mDatabase.addValueEventListener(yourModelListener);
For More Info about Listeners .. https://firebase.google.com/docs/database/android/retrieve-data

Categories

Resources