我在Android应用中使用Firestore。我创建了一个类User
,它表示Firestore中users
集合下的文档。这是课程:
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;
}
}
此类的想法是从Firestore加载(使用方法getDocumentSnapshot
和fromDocumentSnapshot
),然后此类(使用方法attachUpdateEventListeners
)对其自身进行更新检查。问题在于,客户端代码可以在类更新时调用getter方法(由于Firestore触发了更新),因此客户端可能会获得该字段的过时值。
我正在寻找一种编码这样的东西的方法:
public String myGetter()
{
if (isUpdateRunning())
{
waitForUpdate();
}
return myField;
}
但是我不知道如何实现两种方法isUpdateRunning
和waitForUpdate
。
我找到的唯一解决方案是在我的isUpdateRunning
类中放置一个标记User
,该标记将在true
开始时设置为snapshotListener
,并设置为{ {1}}完成时。然后,在我的getter中,如果此标志的值为false
,则在返回值之前,我将等待一段固定的时间(使用true
)。但是,我认为这不是最佳解决方案(也是因为使用Thread.sleep
我将不得不处理Thread.sleep
)。
InterruptedException
答案 0 :(得分:1)
使用这些布尔值并尝试调用sleep(1000)
并不能在所有情况下都对您有所帮助,在这种情况下,这也称为不良做法。如果从数据库获取数据所花费的时间大于1秒,该怎么办?您的应用将无限期冻结,这是糟糕的用户体验。根据您的连接速度,可能需要几百毫秒到几秒钟的时间才能获得该数据。因此,不能保证需要多长时间。
如何等待SnapshotListener完成?
Firebase API为asynchronous
,这意味着onEvent()
方法在调用后立即返回,并且它返回的Task中的回调将在一段时间后调用。
为避免阻塞主线程,此问题的快速解决方案是仅在onEvent()
方法内使用这些结果,否则,如果您要等待数据,建议您查看 post 中我的答案的最后一部分,其中我解释了如何使用自定义回调来完成。您也可以查看此 video 以获得更好的理解。