Firebase Android onAuthStateChanged вызывается дважды
Я начал работать с новым Firebase SDK.
Когда я выполняю вход пользователя в систему, я использую метод onAuthStateChanged дважды с тем же состоянием (и т.д. пользовательский вход).
Я уверен, что добавляю AuthStateListener только один раз к ссылке FirebaseAuth.
Любая помощь?
Ответы
Ответ 1
Да, и это очень раздражает. Это связано с регистрационным звонком. Не только это, onAuthStateChanged будет вызываться много раз во многих разных состояниях, не имея возможности узнать, в каком состоянии оно находится.
Документация говорит:
onAuthStateChanged (FirebaseAuth auth)
Этот метод вызывается в потоке пользовательского интерфейса при изменении состояния аутентификации:
Вот несколько советов по обнаружению текущего состояния:
- Регистрационный вызов: пропустите первый вызов с флагом.
- Пользователь выполнил вход: пользователь из параметра:!= null.
- Пользователь отключен: пользователь из параметра == null.
- Текущие пользовательские изменения: пользователь из параметра is!= null, а последний идентификатор пользователя: = идентификатор пользователя из параметра
- Обновление токена пользователя: пользователь из параметра: = null, а последний идентификатор пользователя - id пользователя из параметра
Этот слушатель является беспорядком и очень ошибкой. Команда Firebase должна изучить ее.
Ответ 2
Мое обходное решение заключается в том, чтобы использовать флаг Boolean, объявленный глобально, для флага, если раньше требовалось onAuthStateChanged.
private Boolean authFlag = false;
mAuthListener = new FirebaseAuth.AuthStateListener() {
@Override
public void onAuthStateChanged(@NonNull final FirebaseAuth firebaseAuth) {
if (firebaseAuth.getCurrentUser() != null) {
if(authFlag== false) {
// Task to perform once
authFlag=true;
}
}
}
};
Ответ 3
Обычно я хочу настроить интерфейс перед добавлением прослушивателя и повторять настройку каждый раз, когда изменяется состояние аутентификации (избегая первоначального двойного вызова). Мое решение состоит в улучшении решения с использованием логического флага и отслеживании идентификатора пользователя (не токена) последнего пользователя, который может быть нулевым.
private FirebaseAuth firebaseAuth;
private String lastUid; // keeps track of login status and changes thereof
В onCreate я получаю экземпляр auth и соответственно устанавливаю пользовательский интерфейс перед добавлением слушателя в onStart
@Override
protected void onCreate(Bundle savedInstanceState){
...
firebaseAuth = FirebaseAuth.getInstance();
getUserSetUI();
...
}
где getUserSetUI устанавливает lastUid в соответствии с экземпляром аутентификации
private void getUserSetUI(){
lastUid = (firebaseAuth == null || firebaseAuth.getCurrentUser() == null) ?
null : firebaseAuth.getUid();
setUI(!(lastUid == null));
}
Слушатель проверяет, действительно ли состояние изменилось
@Override
public void onAuthStateChanged(@NonNull FirebaseAuth auth){
String uid = auth.getUid(); // could be null
if( (uid == null && lastUid != null) || // loggedout
(uid != null && lastUid == null) || // loggedin
(uid != null && lastUid != null && // switched accounts (unlikely)
!uid.equals(lastUid))){
getUserSetUI();
}
}
Ответ 4
В то время как другие ответы, представленные здесь, могли бы справиться с работой, я нахожу управление флагом громоздким и подверженным ошибкам.
Я предпочитаю разоблачать событие в короткие сроки. Очень маловероятно, может быть, даже невозможно, чтобы пользователь вошел в систему, а затем вышел из системы в течение 200 мс, скажем.
TLDR
Отмена означает, что перед обработкой события вы должны подождать, чтобы выяснить, произойдет ли то же событие снова в течение заданного периода времени.
Если это так, вы сбрасываете таймер и ждете снова.
Если это не так, вы обрабатываете событие.
Это вопрос Android, который не относится к моей области, но я уверен, что Android предоставляет какой-то инструмент, который может помочь с этой задачей.
Если нет, вы можете сделать его, используя простой таймер.
Вот как может выглядеть реализация Javascript:
var debounceTimeout;
const DebounceDueTime = 200; // 200ms
function onAuthStateChanged(auth)
{
if (debounceTimeout)
clearTimeout(debounceTimeout);
debounceTimeout = timeout(() =>
{
debounceTimeout = null;
handleAuthStateChanged(auth);
}, DebounceDueTime);
}
function handleAuthStateChanged(auth)
{
// ... process event
}