我在 Android 应用中使用 Firestore。我创建了一个 User
类,它代表 Firestore 中 users
集合下的一个文档。这是类(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;
}
}
此类的想法是从 Firestore 加载它(使用方法 getDocumentSnapshot
和 fromDocumentSnapshot
),然后该类检查自身是否有更新(使用方法 >attachUpdateEventListeners
)。问题是客户端代码可以在类更新时调用 getter 方法(由于 Firestore 触发的更新),因此客户端可能会获取该字段的过时值。
我正在寻找一种编写如下代码的方法:
public String myGetter()
{
if (isUpdateRunning())
{
waitForUpdate();
}
return myField;
}
但我不知道如何实现两个方法 isUpdateRunning
和 waitForUpdate
。
我找到的唯一解决方案是在我的 User
类中放置一个标志 isUpdateRunning
,当snapshotListener
启动并在完成时返回 false
。然后,在我的 getter 中,如果此标志的值为 true
,我将在返回该值之前等待固定的时间(使用 Thread.sleep
)。不过,我认为这不是最好的解决方案(也是因为使用 Thread.sleep 我必须处理 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;
}
最佳答案
您不能使用这些 boolean 值来做到这一点,并且尝试调用 sleep(1000)
并不能在所有情况下为您提供帮助,在这种情况下它也被称为不好的做法。如果从数据库获取数据的时间超过 1 秒,会发生什么情况?您的应用程序将无限期卡住,这是一种糟糕的用户体验。根据您的连接速度,可能需要几百毫秒到几秒钟的时间才能获得数据。因此无法保证需要多长时间。
How to wait until a SnapshotListener has finished?
Firebase API 是异步
,这意味着 onEvent()
方法在调用后立即返回,并且它返回的任务的回调将在一段时间后调用。
为了避免阻塞主线程,解决此问题的一个快速解决方案是仅在 onEvent()
方法中使用这些结果,否则,如果您想等待数据,我建议您查看我的答案的最后一部分 post 其中我解释了如何使用自定义回调来完成它。您还可以看看这个video 为了更好地理解。
关于java - Android - 如何等待 SnapshotListener 完成?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51825131/