Firebase Android: как читать из разных ссылок последовательно

В одном из моих действий на Android мне нужно выполнить несколько запросов к Firebase, чтобы наконец показать что-то пользователю.

Таким образом, мне нужно проверить ссылку " Пользователи", чтобы проверить, на каком этапе курса он находится в данный момент, а затем мне нужно прочитать содержимое курса, чтобы загрузить его.

В настоящее время я занимаюсь тем, что у меня есть два вложенных слушателя:

ref1.addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {

         ref2.addListenerForSingleValueEvent(new ValueEventListener() {
             @Override
             public void onDataChange(DataSnapshot dataSnapshot) {
                  //do the work
             }
         }); 
    }
}); 

Есть ли лучший способ сделать эти запросы, когда они нужны вам последовательно?

1 ответ

TL;DR

Так же, как Parse сделал с Bolts, Google также предоставил структуру задач, которая реализует обещания JavaScript. Таким образом, вместо вложенных слушателей, вы можете создать последовательность задач.

Результат будет отправлен addOnSuccessListener если все задачи выполнены успешно.

Если какой-либо из них завершится неудачно во время выполнения варианта использования, последовательность будет прервана, а исключение передано addOnFailureListener,

public Task<Course> execute() {
    return Tasks.<Void>forResult(null)
        .then(new GetUser())
        .then(new GetCourse());
}

public void updateInBackground() {
    Tasks.<Void>forResult(null)
        .then(new GetUser())
        .then(new GetCourse())
        .addOnSuccessListener(this)
        .addOnFailureListener(this);
}

@Override
public void onFailure(@NonNull Exception error) {
    Log.e(TAG, error.getMessage());
}

@Override
public void onSuccess(Customer customer) {
    // Do something with the result
}

Описание

Предположим, вы хотите скачать два объекта типа User а также Course из Firebase.

Вам необходимо создать первое задание вашей последовательности, используя API-интерфейс Tasks. Ваши варианты:

  • Создайте успешное задание, используя Tasks.forResult
  • Создать TaskCompletionSource, установите значения результата или исключения, затем верните задачу.
  • Создать задачу из callable,

Я предпочитаю первый вариант в основном из-за разборчивости кода. Если вам нужно запустить задачи в вашем собственном исполнителе, вы должны использовать первый или второй вариант.

Теперь давайте создадим два Continuation Задачи два скачать каждый:

class GetUser implements Continuation<Void, Task<User>> {

    @Override
    public Task<User> then(Task<Void> task) {
        final TaskCompletionSource<User> tcs = new TaskCompletionSource();

        ref1.addListenerForSingleValueEvent(new ValueEventListener() {

            @Override
            public void onCancelled(DatabaseError error) {
                tcs.setException(error.toException());
            }

            @Override
            public void onDataChange(DataSnapshot snapshot) {
                tcs.setResult(snapshot.getValue(User.class));
            }

        });

        return tcs.getTask();
    }

}

а также

class GetCourse implements Continuation<User, Task<Course>> {

    @Override
    public Task<Course> then(Task<User> task) {
        final User result = task.getResult();
        final TaskCompletionSource<Course> tcs = new TaskCompletionSource();

        ref2.addListenerForSingleValueEvent(new ValueEventListener() {

            @Override
            public void onCancelled(DatabaseError error) {
                tcs.setException(error.toException());
            }

            @Override
            public void onDataChange(DataSnapshot snapshot) {
                tcs.setResult(snapshot.getValue(Course.class));
            }

        });

        return tcs.getTask();
    }

}

По документации звоните getResult() и разрешить распространению RuntimeExecutionException для распространения ошибки завершенной Задачи.

RuntimeExecutionException будет развернута так, что задание вернулось continueWith(Continuation) или же continueWithTask(Continuation) терпит неудачу с оригинальным исключением.

Согласно блогу Firebase: https://firebase.googleblog.com/2016/09/become-a-firebase-taskmaster-part-3_29.html

Мы могли бы реализовать цепочку асинхронных задач следующим образом:

public Task<ClassReturnedByTask3> wrapAllTask() {
    return Tasks.call(new Task1())
        .continueWithTask(new Task2())
        .continueWithTask(new Task3());
}

Где Задача1 - Задача3 определяются как:

static class Task1 implements Callable<ClassReturnedByTask1> {
    @Override
    public ClassReturnedByTask1 call() throws Exception {
        ClassReturnedByTask1 result = new ClassReturnedByTask1();
        return result;
    }
}

static class Task2 implements Continuation<ClassReturnedByTask1, Task<ClassReturnedByTask2>> {
    @Override
    public Task<ClassReturnedByTask2> then(Task<ClassReturnedByTask1> task) {
        final TaskCompletionSource<ClassReturnedByTask2> tcs = new TaskCompletionSource();

        ClassReturnedByTask1 resultFromTask1 = task.getResult();
        ClassReturnedByTask2 result = new ClassReturnedByTask2();
        tcs.setResult(result);
        return tcs.getTask();
    }
}

static class Task3 implements Continuation<ClassReturnedByTask2, Task<ClassReturnedByTask3>> {
    @Override
    public Task<ClassReturnedByTask3> then(Task<ClassReturnedByTask2> task) {
        final TaskCompletionSource<ClassReturnedByTask3> tcs = new TaskCompletionSource();

        ClassReturnedByTask2 resultFromTask2 = task.getResult();
        ClassReturnedByTask3 result = new ClassReturnedByTask3();
        tcs.setResult(result);
        return tcs.getTask();
    }
}

Чтобы выполнить функцию wrapAllTask ​​(), вы можете запустить:

Task<ClassReturnedByTask3> tasks = wrapAllTask();
tasks.addOnSuccessListener(new OnSuccessListener<ClassReturnedByTask3>() {
    @Override
    public void onSuccess(ClassReturnedByTask3 resultFromTask3) {
        // do something
    }
});
Другие вопросы по тегам