Placing Firebase value event callbacks in a single place - android

I often find myself writing this piece of code again and again in multiple activities when using firebase realtime db:
ValueEventListener v =new ValueEventListener() {
#Override
public void onDataChange (#NonNull DataSnapshot dbSnapshot){
String ourKey="";
String ourValueID="";
for (DataSnapshot childSnap : dbSnapshot.getChildren()) {
String childKey = childSnap.getKey();
if (childKey == null) {
//do some stuff 1 // and break/Continue/return
}
//or we can directly do something here, as we already assured key is present
else if(childKey.equals(ourKey)){
//do some stuff 2 // and break/Continue/return
MyClass myClass =childSnap.getValue(MyClass.class);
if(myClass==null){
//do some stuff 3 // and break/Continue/return
}
else if(myClass.getID().equals(ourValueID)){
//do some stuff 4 // and break/Continue/return
}
else {
//do some stuff 5 // and break/Continue/return
}
}
else {
//do some stuff 6 // and break/Continue/return
}
}
}
#Override
public void onCancelled (#NonNull DatabaseError databaseError){
//do some stuff 7
}
};
although this is suppose to be how firebase works, it makes my code a lot more unreadable and difficult to debug. what could be a good approach to use these callbacks in a way, that i write this code once and de-clutter my code base?An example would be great.

Inside onDataChange(), you can just call a method:
ValueEventListener v =new ValueEventListener() {
#Override
public void onDataChange (#NonNull DataSnapshot dbSnapshot){
String ourKey="";
String ourValueID="";
retrieveDataFromFb(dbSnapshot);
public void retrieveDataFromFb(DataSnapshot dataSnapshot){
for (DataSnapshot childSnap : dbSnapshot.getChildren()) {
String childKey = childSnap.getKey();
if (childKey == null) {
//do some stuff 1 // and break/Continue/return
}
else if(childKey.equals(ourKey)){
MyClass myClass =childSnap.getValue(MyClass.class);
}
}

From what I understand you want to store all DB methods in a separate class so that you can reuse these methods which would make the code look cleaner and you are trying to get callback values when they get returned from firebase.
There can be many ways to handle callbacks for events what I recommend is to use an interface it will modularize your code and make it look cleaner, so what you can do is to store the DB methods in a separate class (say FirebaseDB), create your methods there and use an interface to get the callbacks. An example on how you can do it:-
Create an Interface either in the class or separate from the class
public class FirebaseDB {
//This is your interface
public interface DBCallbacklistner {
void onCallback(Map<String, Object> keyMap);
}
public void getkeys(String any_value_you_need_to_pass, DBCallbacklistner dbCallbacklistner){
//I have used a different method here you can use your releveant method here
database.somemethod(any_value_you_need_to_pass, new EventListener<DocumentSnapshot>() {
#Override
public void onEvent(#Nullable DocumentSnapshot documentSnapshot) {
//Suppose you receive the callback here
if(documentSnapshot.exists()){
Map<String, Object> keysMap = (HashMap<String, Object>) documentSnapshot.getData();
//Pass the callback in your interface
dbCallbacklistner.onCallback(keysMap);
}
}
});
}
}
Use that interface wherever you want
Using the function from the class call that interface and use the values
mFirebaseDBObject.getkeys(value, new FirebaseDB.DBCallbacklistner() {
#Override
public void onCallback(Map<String, Object> keyMap) {
if (keyMap != null) {
//Use your keymap here
}
}
});
One more thing I want to point out is that If there are too many callbacks for different calls, I suggest to make separate interfaces based on logical seperation of callbacks.
Because if there are many callbacks in a single interface you would have to override each one of them, whether you require it or not.

For the time being, i am using the following approach:
Suppose my firebase db consists of a list of objects which can be deserialised to the following format:
class MyClass{
public String myClassUniqueID;
... other attributes;
}
For the db i will be handling all value event listener's lifecycle in my own activity(i.e attaching to the db refernce via dbRef.addValueEventListener(dbListener); or dbRef.removeEventListener(dbListener);, But the process of creating this dbListener and passing it the neccessary tasks to be done would be managed in the following utility function :
public interface DbListenerActions {
void onMyClassObjFound(#NonNull MyClass matchedObj);
default void onMyClassObjNOTFound() {
}
}
public static ValueEventListener getMyClassObjectFinderListener(String id, DbListenerActions actions) {
Log.e(TAG, "onDataChange: our id:" + id);
ValueEventListener dbListener = new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dbSnapshot) {
for (DataSnapshot currChildSnap : dbSnapshot.getChildren()) {
String currChildKey = currChildSnap.getKey();
MyClass currChildValue = currChildSnap.getValue(MyClass.class);
if (currChildKey == null) {
Log.e(TAG, "onDataChange: currChildKey is null. continuing");
continue;
}
if (currChildValue == null) {
Log.e(TAG, "onDataChange: currChildValue is null.continuing");
continue;
}
if (currChildValue.myClassUniqueID.equals(id)) {
Log.e(TAG, "onDataChange: currChildValue id matches our id ");
Log.e(TAG, "onDataChange: performing action and RETURNING(i.e getting out of this callback)");
//do stuff here
actions.onMyClassObjFound(currChildValue);
return;
} else {
Log.e(TAG, "onDataChange: current obj DOES NOT matches our id. continuing");
Log.e(TAG, "onDataChange: current object ID :" + currChildValue.myClassUniqueID);
Log.e(TAG, "onDataChange: --------------------------------------------------------------");
continue;
}
}
Log.e(TAG, "onDataChange: user not found, performing not found action" );
actions.onMyClassObjNOTFound();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
};
return dbListener;
}
In this way, i am able to get the necessary log info that i want during my debugging and since there are only 2 possible actions that i want to perform, i get a lot more assurance on the working of my listener. 50 lines of necessary but redundant code from 11 activities combined to just 1 utility function!
Now all i need to write is this small , more easy to debug piece of code in each of my activity:
ValueEventListener dbListener=getMyClassObjectFinderListener("some_id", new DbListenerActions() {
#Override
public void onMyClassObjFound(#NonNull MyClass matchedObj) {
//callSomeFunction()
// callSomeOtherFunction(matchedObj)
//...
}
});
Since i made the onMyClassObjNOTFound(..) function default i don't even need to provide that unless i really want to perform some action there. So this whole thing is working nicely for me :D
I asked on twitter about this thing too, somebody told me that an abstract class could also be used for such thing. I didn't got to research more there, but if someone knows about that approach too, then let me know!

Related

Firebase / Android - Wait for all thread finish for response

I am have the follow code:
public synchronized void next(final RoomListQueryResultHandler handler) {
this.setLoading(true);
roomList = new ArrayList<Room>();
this.database.child("members").child(this.mUser.getUid()).child("rooms")
.limitToFirst(this.mLimit)
.startAt(this.currentPage * this.mLimit)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
RoomListQuery.this.setLoading(false);
//mListAdapter.setLoading(false);
if (!dataSnapshot.hasChildren()) {
RoomListQuery.this.currentPage--;
}
for (DataSnapshot ds : dataSnapshot.getChildren()) {
Room room = ds.getValue(Room.class);
//roomList.add(Room.upsert(room));
Room.getRoom(room.getId(), new Room.RoomGetHandler() {
#Override
public void onResult(Room room, customException e) {
if (e != null) {
// Error!
e.printStackTrace();
return;
}
roomList.add(room);
}
});
handler.onResult(roomList, (customException) null);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
handler.onResult((List) null, new customException(databaseError.toString()));
}
});
}
}
If they are see, I have two Handlers, at first I call a list of "rooms" from Firebase, and then for each one I get the detail in other query.
The problem is that the response is a empty list, since the function not wait for all query details to be executed for the rooms, so the variable roomList always returns empty.
Any idea what I can implement, or what other methodology to use to solve it?
Thank you very much!
Greetings.
Depending on how your application is structured, you might want to change the database design so that there is no need to perform an additional Firebase query for each room retrieved from the first query.
//mListAdapter.setLoading(false);
If you're creating a list view where each row is from the /members/<user_id>/rooms Firebase node, what are the minimum room attributes necessary to display that list? If it's just a few things like room name, photo url, owner, room_id, etc you might be better off duplicating those from the original source. Then clicking one of those rows can trigger the original additional Firebase query you had as part of Room.getRoom(room.getId(), new Room.RoomGetHandler() { ... });, to navigate to a new screen / display a modal with the full room details once retrieved.
Update
To address your comment about requiring the extra data, in that case, as part of the Room class I would include an extra boolean value _loadedDetails set initially to false. So that for rendering a room within the list, when _loadedDetails is currently false just display a loading spinner. That way you can still perform those additional queries and when completed, update the appropriate Room object within roomList based on the index. Something like this:
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
RoomListQuery.this.setLoading(false);
//mListAdapter.setLoading(false);
if (!dataSnapshot.hasChildren()) {
RoomListQuery.this.currentPage--;
}
int i = 0;
for (DataSnapshot ds : dataSnapshot.getChildren()) {
Room room = ds.getValue(Room.class);
roomList.add(room); // here instead
updateRoom(room, i);
i++;
}
handler.onResult(roomList, (customException) null);
}
...
// outside of the ValueEventListener
public void updateRoom(room, index) {
Room.getRoom(room.getId(), new Room.RoomGetHandler() {
#Override
public void onResult(Room room, customException e) {
if (e != null) {
// Error!
e.printStackTrace();
return;
}
room._loadedDetails = true; // make that publicly accessible boolean, or include a setter method instead
roomList.set(index, room);
}
});
}

Working with EventListener in Firebase for Android

Consider, I store the following Key-Value pair in Firebase Database.
Key: "CarBrand" and Value: "Audi"
I read the Value for this Key from Firebase Database and display the Value in TextView. For this, I use an EventListener.
The problem I face with an EventListsner is since it runs on a separate thread, the TextView returns a NullPointerException even before the Value is fetched from Firebase.
To overcome the issue I have been using this dirty trick (using a Handler with 500 to 1000 ms delay). Can someone guide me the right way to fetch and display the Value in TextView or any other Views
PS: Apologies for this codeless Question
Thanks
EDIT: More the Code, better the question. The sample code is as follows
String CardBrand;
private ValueEventListener contentListener() {
contentListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//Pull Values for all available Keys
CarBrand = dataSnapshot.child("CarBrand").getValue(String.class);
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
return contentListener;
}
dbContent.addListenerForSingleValueEvent(contentListener());
mTextView.setText(CarBrand);
The above code results in error. To avoid this, I replaced the last line of code with
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
mTextView.setText(CarBrand);
}
}, 1000);
So this is what I have been doing. What would be the right way to display the Text in TextView in my Case?
Your approach is wrong as the listener is updating the String CarBrand But since the code is not in sync and casing the Error. You need to update the code
private ValueEventListener contentListener() {
contentListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//Pull Values for all available Keys
String CarBrand =
dataSnapshot.child("CarBrand").getValue(String.class);
updateBrand(CarBrand);
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
return contentListener;
}
dbContent.addListenerForSingleValueEvent(contentListener());
you need to create a Function to update the value
/* updates the Car brand Text
TODO:: You can do anystuff you want to do after anything gets
updated
*/
public void updateBrand(String brand){
mTextView.setText(CarBrand);
}
NOTE : Put Listener on the Data you are actually looking for changes.
If you want to work with the result of some callback (like ValueEventListener) you should execute your code inside the overriding methods.
String cardBrand;
private ValueEventListener contentListener() {
contentListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//Pull Values for all available Keys
carBrand = dataSnapshot.child("CarBrand").getValue(String.class);
mTextView.setText(carBrand);
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
return contentListener;
}
dbContent.addListenerForSingleValueEvent(contentListener());
Let me show you small example:
This class responsible for retreiving the link for you, and it doesn't know what will you do with it, so it takes Callback and pass the result to your callback, where you can do anything you want.
public class SomeDataSource {
void getData(Callback callback) {
String url = Backend.GetUrl();
callback.onDownloadUrlReceived(url);
}
}
this is description of callback. Which you can implement to handle data from SomeDataSource
public interface Callback {
void onDownloadUrlReceived(String url)
}
usage
void onCreate(Bundle savedInstanceState) {
SomeDataSource downloader = new SomeDataSource();
//here callback is implemented, and you can handle data from SomeDataSource like you want.
downloader.getData(new Callback() {
#Override
public void onDownloadUrlReceived(String url) {
do wat you want with url
}
});
}

Android for loop not looping ? Firebase

I have an issue with the following code : I don't understand why my for-loop doesn't loop before if (gameExists[0] == false){... is called.
Button playButton = (Button) findViewById(R.id.play_button);
playButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
user = mFirebaseAuth.getCurrentUser();
final String gameCateg = String.valueOf(categorySelected[0]);
DatabaseReference allExistingGamesToMatchKeys = mFirebaseDatabase.getReference().child("gamestomatchkeys");
allExistingGamesToMatchKeys.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final boolean[] gameExists = {false};
Log.d("gameExists before loop ", String.valueOf(gameExists[0]));
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
final String currentKey = postSnapshot.getKey();
//check player 2 is not the same as player 1 so that we don't match the same player
if(gameCateg.equals(postSnapshot.getValue().toString())){
mFirebaseDatabase.getReference().child("games").child(currentKey).child("player1").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String namePlayer1 = dataSnapshot.getValue(String.class);
if(!(user.getUid().toString().equals(namePlayer1))){
mFirebaseDatabase.getReference().child("gamestomatchkeys").child(currentKey).removeValue();
mFirebaseDatabase.getReference().child("games").child(currentKey).child("player2").setValue(user.getUid());
gameExists[0] = true;
Log.d("gameExists in for loop ", String.valueOf(gameExists[0]));
Intent intent = new Intent(getApplicationContext(), Game.class);
intent.putExtra("gameIdKey", currentKey);
startActivity(intent);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
break;
}
}
if (gameExists[0] == false){
Log.d("gameExists in if", String.valueOf(gameExists[0]));
This is what I get in my logs, in this order :
gameExists before loop: false
gameExists in if: false
gameExists in for loop: true
I don't understand why I get
gameExists in if: false
before
gameExists in for loop: true
I want my loop to be called and entirely looped before if (gameExists[0] == false){..., what should I modify ?
Thank you !
To make it simple, Firebase Database request are outside of the code flow. Take a look at this:
// here is first point
allExistingGamesToMatchKeys.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// here is second point
}
...
}
// here is third point
Above example will be executed like this
--> first point --> third point
Then where is the second point? Second point will be executed whenever Firebase get data from online database, so it is outside the flow (but most of the time, it will be executed after the third point.
So in conclusion, if you need some code to be executed after Firebase requests is done, place it inside onDataChange()
You might look at this for reference
It's because "gameExists in for loop" is not actually in the for loop. It's in a callback that is created in the for loop.
mFirebaseDatabase.getReference().child("games").child(currentKey).child("player1").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String namePlayer1 = dataSnapshot.getValue(String.class);
if(!(user.getUid().toString().equals(namePlayer1))){
mFirebaseDatabase.getReference().child("gamestomatchkeys").child(currentKey).removeValue();
mFirebaseDatabase.getReference().child("games").child(currentKey).child("player2").setValue(user.getUid());
gameExists[0] = true;
Log.d("gameExists in for loop ", String.valueOf(gameExists[0]));
Intent intent = new Intent(getApplicationContext(), Game.class);
intent.putExtra("gameIdKey", currentKey);
startActivity(intent);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Here you are creating a new instance of a ValueEventListener and you are overriding methods within it. Your are not executing the code within those methods at the point of instantiation. That code get called whenever your ValueEventListener decides to call the onDataChange() method.
I'm not entirely sure what this object is, or how it works:
mFirebaseDatabase.getReference().child("games").child(currentKey).child("player1")
But if I were you, I would start by seeing if I could get a DataSnapshot from it, and using that snapshot on whatever code you want to execute outside of the Listener.
If I had to guess, there might be a method like:
mFirebaseDatabase.getReference().child("games").child(currentKey).child("player1").getDataSnapshot();
that you can call. Then copy all the code from inside onDataChange() to outside of the listener.
(I have no idea if that method exits, but I would assume there is some way of getting the current DataSnapshot)

Continue execution after data received from multiple location in Firebase

I know this might be a common question but I am really stuck at this point.
I am receiving data from 2 multiple locations and after I received from both I need to continue executing and than return that data to the calling method.
I am aware of this thread: https://stackoverflow.com/a/33204705/1820644 but it doesn't fit here actually as I need to return the data to the calling method.
For the method that blocks UI thread I can call it from AsyncTask, there is no problem. But how can I return data to the calling method that I have successfully completed execution.
This is inside my helper class
// This method should be called in AsyncTask
public boolean doComputation() {
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("activity")
.child(id);
ref.child("path1").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Call 1 completed
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
ref.child("path2").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Call 2 completed
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
// Some more to do
// Need to return true after execution complete
return true;
}
I can not return true inside of the onDataChange as it will counted for onDataChange method and not for doComputation.
I can perform the calculation by moving it to another method and after each onDataChange callback I can check for variable count, and if it is 2 I can perform the calculations. But after it completes I need to notify it to the calling method that execution is completed.
This is a little bit tricky with Firebase. But, I am really stuck at it right now. Any help will be much appreciated. Thank you.
I have gone with the Tasks API which Firebase uses already. Its great.
As mentioned by #qbix , This answer does the same thing. The example in the answer explains good.
You can also find video link of this API instructions here.
I have tried and tested it. Solves my problem.
Depends on the case, what I usually do is to set a flag after each listener completed its job and then call a method to check the flags. If they are all completed, then do the next operation.
For example
private Boolean listener1Completed;
private Boolean listener2Completed;
public void addListeners() {
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("activity")
.child(id);
ref.child("path1").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
listener1Completed = true;
checkListenerStatus();
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
ref.child("path2").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
listener2Completed = true;
checkListenerStatus();
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
}
private void checkListenerStatus() {
if (listener1Completed && listener2Completed) {
// do computation
}
}
Since firebase works in another thread you can't return desired result instantly. You have to create callback to notify the caller when your result already received. You can achieve this using interface read here
Another way. You can get result from Asynctask
Return a value from AsyncTask in Android
Or you can pass your class in parameter (not in AsyncTask job)
public void doComputation(yourCallerClass cls) {
//firebase result.....void
cls.YourResult(trueOrFalse);
.....
}
in your caller class instance eg. yourCallerClass
...
public void YourResult(boolean result){
// do stuff
}

Is it possible to synchronously load data from Firebase?

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

Categories

Resources