Могу ли я заставить watchSingleEvent из сети вместо использования кеша (Firebase)? [Дубликат]

Если вы похожи на меня, когда я впервые начал использовать Java, я хотел использовать оператор «==» для проверки того, были ли два экземпляра String одинаковыми, но к лучшему или худшему это не правильный способ сделать это в Java.

В этом уроке я продемонстрирую несколько разных способов правильно сравнить строки Java, начиная с подхода, который я использую большую часть времени. В конце этого руководства по сопоставлению Java String я также обсужу, почему оператор «==» не работает при сравнении строк Java.

Вариант 1: Сравнение строк Java с методом equals Большая часть (возможно, в 95% случаев). Я сравниваю строки с методом equals класса Java String следующим образом:

if (string1.equals(string2))

Этот метод String равен методу для двух строк Java, и если они содержат точно такую ​​же строку символов, они считаются равными.

Взглянув на быстрый пример сравнения строк с методом equals, если был выполнен следующий тест, две строки не будут считаться равными, поскольку символы не являются точно такими же (случай символов различен):

String string1 = "foo";
String string2 = "FOO";

if (string1.equals(string2))
{
    // this line will not print because the
    // java string equals method returns false:
    System.out.println("The two strings are the same.")
}

Но, когда две строки содержат одну и ту же строку символов, метод equals вернет true, как в этот пример:

String string1 = "foo";
String string2 = "foo";

// test for equality with the java string equals method
if (string1.equals(string2))
{
    // this line WILL print
    System.out.println("The two strings are the same.")
}

Вариант 2: Сравнение строк с методом equalsIgnoreCase

В некоторых строковых сравнительных тестах вы захотите игнорируйте, являются ли строки строчными или строчными. Если вы хотите проверить свои строки на равенство в этом случае нечувствительным образом, используйте метод equalsIgnoreCase класса String, например:

String string1 = "foo";
String string2 = "FOO";

 // java string compare while ignoring case
 if (string1.equalsIgnoreCase(string2))
 {
     // this line WILL print
     System.out.println("Ignoring case, the two strings are the same.")
 }

Вариант 3: сравнение строк Java с методом compareTo

Существует также третий, менее распространенный способ сравнения строк Java, и это с методом сравнения String класса. Если две строки точно совпадают, метод compareTo вернет значение 0 (ноль). Ниже приведен краткий пример того, как выглядит этот метод сравнения строк:

String string1 = "foo bar";
String string2 = "foo bar";

// java string compare example
if (string1.compareTo(string2) == 0)
{
    // this line WILL print
    System.out.println("The two strings are the same.")
}

Пока я пишу об этой концепции равенства в Java, важно отметить, что язык Java включает в себя метод equals в базовый класс Java Object. Всякий раз, когда вы создаете свои собственные объекты, и вы хотите предоставить средства для проверки того, являются ли два экземпляра вашего объекта «равными», вы должны переопределить (и реализовать) этот метод equals в своем классе (точно так же, как язык Java предоставляет это равенство / сравнение в методе String равно).

Вы можете посмотреть на это ==, .equals (), compareTo () и compare ()

49
задан Grimthorr 15 November 2017 в 09:40
поделиться

3 ответа

Как работает постоянство

Клиент Firebase хранит копию всех данных, которые вы активно слушаете в памяти. После того, как последний прослушиватель отключится, данные будут сброшены из памяти.

Если вы включите жесткость диска в приложении Android Firebase с помощью:

Firebase.getDefaultConfig().setPersistenceEnabled(true); 

Клиент Firebase будет хранить локальную копию (на диске) всех данных, которые приложение недавно прослушивало.

Что происходит, когда вы присоединяете прослушиватель

. У вас есть следующие ValueEventListener:

ValueEventListener listener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot snapshot) {
        System.out.println(snapshot.getValue());
    }

    @Override
    public void onCancelled(FirebaseError firebaseError) {
        // No-op
    }
};

Когда вы добавите ValueEventListener в местоположение:

ref.addValueEventListener(listener); 
// OR
ref.addListenerForSingleValueEvent(listener); 

Если значение местоположения находится в кеше локального диска, клиент Firebase немедленно вызовет onDataChange() для этого значение из локального кеша. Если затем инициирует проверку с сервером, запрашивать какие-либо обновления для значения. Он может впоследствии снова вызвать onDataChange(), если произошла смена данных на сервере с момента последнего добавления в кэш.

Что происходит, когда вы используете addListenerForSingleValueEvent

Когда вы добавляете прослушиватель событий одного значения в одно и то же место:

ref.addListenerForSingleValueEvent(listener);

Клиент Firebase (как и в предыдущей ситуации) сразу вызывает onDataChange() для значения из локального дискового кэша. Он не еще раз вызовет onDataChange(), даже если значение на сервере окажется другим. Обратите внимание, что обновленные данные по-прежнему будут запрашиваться и возвращаться при последующих запросах.

Это было рассмотрено ранее в . Как работает синхронизация Firebase с общими данными?

Решение и обходное решение

Лучшим решением является использование addValueEventListener() вместо однозначного прослушивателя событий. Регулярный приемник значений получит как локальное событие, так и потенциальное обновление с сервера.

В качестве обходного пути вы также можете вызывать keepSynced(true) в местах, где вы используете однозначный прослушиватель событий. Это гарантирует, что данные обновляются каждый раз, когда они меняются, что значительно повышает вероятность того, что ваш однозначный слушатель событий увидит текущее значение.

62
ответ дан Frank van Puffelen 16 August 2018 в 00:03
поделиться
  • 1
    Спасибо. Теперь я понимаю из ответа ссылки. – Jason Hoch 28 December 2015 в 05:08
  • 2
    – Eslam Sameh Ahmed 31 December 2016 в 17:38
  • 3
    Какое неудобное поведение делает его практически невозможным. – Dirty Henry 28 February 2017 в 10:09
  • 4
    Вызов keepSynced(true), а затем с помощью .addListenerForSingleValueEvent(listener); не работает, когда приложение открывается в первый раз после изменения базы данных. Для регистрации требуется дважды открывать приложение. – Drew Szurko 29 April 2017 в 17:59
  • 5
    @Drew это действительно работает, когда вы делаете один вызов после & quot; keepSynced & quot ;. Создайте запрос, установите keepSynced, addValue, например, и onCompleted keepValue false, чтобы не синхронизировать все время. Затем данные обновляются (не проверял, работает ли он каждый раз, как это необходимо). Другое решение, которое, кажется, работает, - это мой ответ в этом сообщении – Janusz Hain 13 August 2017 в 06:12

Когда workinkg with persistence включен, я подсчитал время, когда слушатель получил вызов onDataChange () и остановился для прослушивания в 2 раза. Работал для меня, может быть, помогает:

private int timesRead;
private ValueEventListener listener;
private DatabaseReference ref;

private void readFB() {
    timesRead = 0;
    if (ref == null) {
        ref = mFBDatabase.child("URL");
    }

    if (listener == null) {
        listener = new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                //process dataSnapshot

                timesRead++;
                if (timesRead == 2) {
                    ref.removeEventListener(listener);
                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
            }
        };
    }
    ref.removeEventListener(listener);
    ref.addValueEventListener(listener);
}
0
ответ дан german 16 August 2018 в 00:03
поделиться

Вы можете создать транзакцию и прервать ее, тогда onComplete будет вызываться, когда он находится в сети (данные nline) или в автономном режиме (кешированные данные)

Я ранее создал функцию, которая работала, только если база данных получила соединение, достаточное для выполнения синхронизация. Я исправил проблему, добавив тайм-аут. Я буду работать над этим и проверить, работает ли это. Возможно, в будущем, когда я получу свободное время, я создам андроидскую библиотеку и опубликую ее, но к тому времени это код в kotlin:

/**
     * @param databaseReference reference to parent database node
     * @param callback callback with mutable list which returns list of objects and boolean if data is from cache
     * @param timeOutInMillis if not set it will wait all the time to get data online. If set - when timeout occurs it will send data from cache if exists
     */
    fun readChildrenOnlineElseLocal(databaseReference: DatabaseReference, callback: ((mutableList: MutableList<@kotlin.UnsafeVariance T>, isDataFromCache: Boolean) -> Unit), timeOutInMillis: Long? = null) {

        var countDownTimer: CountDownTimer? = null

        val transactionHandlerAbort = object : Transaction.Handler { //for cache load
            override fun onComplete(p0: DatabaseError?, p1: Boolean, data: DataSnapshot?) {
                val listOfObjects = ArrayList<T>()
                data?.let {
                    data.children.forEach {
                        val child = it.getValue(aClass)
                        child?.let {
                            listOfObjects.add(child)
                        }
                    }
                }
                callback.invoke(listOfObjects, true)
            }

            override fun doTransaction(p0: MutableData?): Transaction.Result {
                return Transaction.abort()
            }
        }

        val transactionHandlerSuccess = object : Transaction.Handler { //for online load
            override fun onComplete(p0: DatabaseError?, p1: Boolean, data: DataSnapshot?) {
                countDownTimer?.cancel()
                val listOfObjects = ArrayList<T>()
                data?.let {
                    data.children.forEach {
                        val child = it.getValue(aClass)
                        child?.let {
                            listOfObjects.add(child)
                        }
                    }
                }
                callback.invoke(listOfObjects, false)
            }

            override fun doTransaction(p0: MutableData?): Transaction.Result {
                return Transaction.success(p0)
            }
        }

В коде, если установлен тайм-аут, тогда I настройте таймер, который вызовет транзакцию с прерыванием. Эта транзакция будет вызываться даже в автономном режиме и будет предоставлять онлайн-или кешированные данные (в этой функции действительно существует вероятность того, что эти данные будут кэшированы). Затем я делаю транзакцию с успехом. OnComplete будет вызван ТОЛЬКО, если мы получим ответ от базы данных firebase. Теперь мы можем отменить таймер (если не null) и отправить данные для обратного вызова.

Эта реализация делает dev на 99% уверенным, что данные находятся в кеше или находятся в режиме онлайн.

Если вы хотите ускорить работу в автономном режиме (чтобы не допустить тупо с тайм-аутом, когда, очевидно, база данных не подключена), проверьте, подключена ли база данных до использования функции выше:

DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected");
connectedRef.addValueEventListener(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot snapshot) {
    boolean connected = snapshot.getValue(Boolean.class);
    if (connected) {
      System.out.println("connected");
    } else {
      System.out.println("not connected");
    }
  }

  @Override
  public void onCancelled(DatabaseError error) {
    System.err.println("Listener was cancelled");
  }
});
0
ответ дан Janusz Hain 16 August 2018 в 00:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: