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
}
});