I'm using Firestore in my Android app. I've created a class User
, which represents a document under the users
collection in my Firestore. This is the class:
public class User
{
private String documentID;
private String email;
private String firstName;
private String lastName;
private String mobilePhone;
private static final String USERS_COLLECTION_NAME = "users";
private static class FieldNames
{
private static final String EMAIL = "email";
private static final String FIRST_NAME = "firstName";
private static final String LAST_NAME = "lastName";
private static final String MOBILE_PHONE = "mobilePhone";
}
private User(String documentID)
{
this.documentID = documentID;
}
private static void checkRequiredStringField(DocumentSnapshot documentSnapshot, String fieldName) throws IllegalArgumentException
{
checkArgument(documentSnapshot.contains(fieldName), "The given documentSnapshot does not contain the required field '%s'.", fieldName);
}
private void attachUpdateEventListeners()
{
FirebaseFirestore.getInstance()
.collection(USERS_COLLECTION_NAME)
.document(documentID)
.addSnapshotListener((documentSnapshot, exception) -> {
if (exception == null)
{
email = documentSnapshot.getString(FieldNames.EMAIL);
firstName = documentSnapshot.getString(FieldNames.FIRST_NAME);
lastName = documentSnapshot.getString(FieldNames.LAST_NAME);
mobilePhone = documentSnapshot.getString(FieldNames.MOBILE_PHONE);
}
else
{
Crashlytics.log(Log.ERROR, createLogTagForClass(getClass()), "Exception occurred while listening for changes on document with ID " + documentID + ". Exception message: " + exception.getMessage());
Crashlytics.logException(exception);
}
});
}
@NonNull
public static Task<DocumentSnapshot> getDocumentSnapshot(@NonNull FirebaseUser firebaseUser)
{
checkNotNull(firebaseUser);
return FirebaseFirestore.getInstance().collection(USERS_COLLECTION_NAME)
.document(firebaseUser.getUid())
.get();
}
public static User fromDocumentSnapshot(@NonNull DocumentSnapshot documentSnapshot) throws IllegalArgumentException
{
checkNotNull(documentSnapshot);
checkRequiredStringField(documentSnapshot, FieldNames.EMAIL);
checkRequiredStringField(documentSnapshot, FieldNames.FIRST_NAME);
checkRequiredStringField(documentSnapshot, FieldNames.LAST_NAME);
checkRequiredStringField(documentSnapshot, FieldNames.MOBILE_PHONE);
User user = new User(documentSnapshot.getId());
user.email = documentSnapshot.getString(FieldNames.EMAIL);
user.firstName = documentSnapshot.getString(FieldNames.FIRST_NAME);
user.lastName = documentSnapshot.getString(FieldNames.LAST_NAME);
user.mobilePhone = documentSnapshot.getString(FieldNames.MOBILE_PHONE);
user.attachUpdateEventListeners();
return user;
}
public String getEmail()
{
return email;
}
public String getFirstName()
{
return firstName;
}
public String getLastName()
{
return lastName;
}
public String getMobilePhone()
{
return mobilePhone;
}
}
The idea of this class is to load it from Firestore (with the methods getDocumentSnapshot
and fromDocumentSnapshot
), then this class checks itself for updates (with the method attachUpdateEventListeners
). The problem is that client code can call a getter method while the class is updating (due to an update fired by Firestore), so the client may get an out-dated value of the field.
I'm looking for a way to code something like this:
public String myGetter()
{
if (isUpdateRunning())
{
waitForUpdate();
}
return myField;
}
but I can't figure out how to implement the two methods isUpdateRunning
and waitForUpdate
.
The only solution I've found is to put a flag isUpdateRunning
in my User
class, which will be set to true
when the snapshotListener
starts and to false
when it finishes. Then, in my getters, if this flag's value is true
I will wait a fixed amount of time (with a Thread.sleep
) before returning the value. Though, I don't think this is the best solution (also because with Thread.sleep
I would have to handle the InterruptedException
).
private boolean isUpdateRunning;
private User(String documentID)
{
this.documentID = documentID;
isUpdateRunning = false;
}
private void attachUpdateEventListeners()
{
FirebaseFirestore.getInstance()
.collection(USERS_COLLECTION_NAME)
.document(documentID)
.addSnapshotListener((documentSnapshot, exception) -> {
if (exception == null)
{
isUpdateRunning = true;
// omitted code
// re-assigning fields...
isUpdateRunning = false;
}
// omitted code
});
}
public String myGetter()
{
if (isUpdateRunning)
{
Thread.sleep(1000); // how to handle the InterruptedException?
}
return myField;
}