I\'m new to Firebase and I\'m having a lot of problems with the fact that all the tasks are called asynchronously.
For example, I am trying to use fetchProvidersForE
When you call fetchProvidersForEmail
that information is not available in the APK of your app. The Firebase client has to call out to the servers to get this information.
Given the nature of the internet, this means that it will take an undetermined amount of time before the result comes back from those servers.
The client has a few options on what to do in the meantime:
Waiting for the data would mean that your code stays simple. But it also means that your app is blocked while the data is being looked up. So: no spinner animation would run, the user can't do anything else (which may be fine for your app, but not for others), etc. This is considered a bad user experience. So bad in fact, that Android will show an Application Not Responding dialog if your app is in this state for 5 seconds.
So instead, the Firebase SDKs choose the other option: they let your code continue, while they're retrieveing the data from the servers. Then when the data is retrieved, they call back into a code block you provided. Most modern web APIs are built this way, so the sooner you come to grips with it, the sooner you can efficiently use those APIs.
The easiest way I found to grasps asynchronous programming is by reframing your problems. Right now you're trying to "first determine if the email is already used, then sign the user up or in".
if (emailIsRegistered(email)) {
signInUser(email);
}
else {
signUpUser(email);
}
This approach leads to a emailIsRegistered
method that returns a boolean, something that is impossible with asynchronous methods.
Now let's reframe the problem to "determine if the email is already used. When we know this, sign the user up or in".
This leads to a different piece of code:
public static boolean emailIsRegistered(String email) {
FirebaseAuth auth = FirebaseAuth.getInstance();
auth.fetchProvidersForEmail(email).addOnCompleteListener(new OnCompleteListener<ProviderQueryResult>() {
@Override
public void onComplete(@NonNull Task<ProviderQueryResult> task) {
if (task.getResult().getProviders().size() > 0) {
signUserIn(email);
}
signUserUp(email);
}
});
We've moved the calls to sign the user up or in into the emailIsRegistered
method and invoke then when the result becomes available.
Now this of course hard-coded the follow up action into the emailIsRegistered
method, which makes it harder to re-use. That's why you quite often see a callback being passed into these functions. A great example of that is the OnCompleteListener
that you're already using. Once the Firebase client gets the result from the servers, it calls the onComplete
method that you passed in.
Learning to deal with asynchronous calls is both hard and important. I'm not sure if this is my best explanation of the concepts ever. So I'll include some previous explanations (from both me and others):