简体   繁体   English

Android-如何等待SnapshotListener完成?

[英]Android - How to wait until a SnapshotListener has finished?

I'm using Firestore in my Android app. 我在Android应用中使用Firestore。 I've created a class User , which represents a document under the users collection in my Firestore. 我创建了一个User类,它表示Firestore中users集合下的文档。 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 ). 此类的想法是从Firestore(使用getDocumentSnapshotfromDocumentSnapshot方法)加载fromDocumentSnapshot ,然后此类(使用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. 问题在于,客户端代码可以在类更新时调用getter方法(由于Firestore触发了更新),因此客户端可能会获取该字段的过时值。

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 . 但我不知道如何实现这两个方法isUpdateRunningwaitForUpdate

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. 我发现的唯一解决方案是在User类中放置一个标志isUpdateRunning ,当snapshotListener启动时将其设置为true ,而在完成时将其设置为false 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. 然后,在我的getter中,如果此标志的值为true我将等待固定的时间(使用Thread.sleep ),然后再返回该值。 Though, I don't think this is the best solution (also because with Thread.sleep I would have to handle the InterruptedException ). 虽然,我认为这不是最好的解决方案(也是因为使用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;
}

You cannot do that using those booleans and trying to call sleep(1000) isn't also helping you in all situations and its also called a bad practice in this case. 您不能使用这些布尔值来做到这一点,并且尝试调用sleep(1000)并不能在所有情况下都对您有帮助,在这种情况下,这也称为不良做法。 What is happening if the time that it takes to get the data from the database is greater than 1 second? 如果从数据库获取数据所花费的时间大于1秒,该怎么办? Your app will indefinitely freeze, which is a terrible user experience. 您的应用将无限期冻结,这是糟糕的用户体验。 Depending on your connection speed it may take from a few hundred milliseconds to a few seconds before that data is available. 根据您的连接速度,可能需要几百毫秒到几秒钟的时间才能获得该数据。 So there are no guarantees about how long it will take. 因此,不能保证需要多长时间。

How to wait until a SnapshotListener has finished? 如何等待SnapshotListener完成?

Firebase APIs are asynchronous , meaning that onEvent() method returns immediately after it's invoked, and the callback from the Task it returns, will be called some time later. Firebase API是asynchronous ,这意味着onEvent()方法在调用后立即返回,并且它返回的Task中的回调将在一段时间后调用。

To avoid blocking the main thread, a quick solve for this problem would be to use those results only inside the onEvent() method, otherwise, if you'll want to wait for the data, I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. 为了避免阻塞主线程,此问题的快速解决方案是仅在onEvent()方法中使用这些结果,否则,如果您要等待数据,建议您查看anwser的最后一部分从这篇文章中,我已经解释了如何使用自定义回调来完成。 You can also take a look at this video for a better understanding. 您也可以观看此视频 ,以更好地理解。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM