RxJava - Оператор задача или вся цепочка задач?
Я пишу код для вставки записи в базу данных Sqlite (если таблица пуста). Прежде чем вставить какие-либо данные, он делает вызов веб-службы LoveToDo.basecampClient().fetchMe()
вернуть некоторые данные.
Я использую SqlBrite для доступа к базе данных и Retrofit для веб-доступа. Вот мой код:
Observable.just(LoveToDo.briteDatabase())
.map(new Func1<BriteDatabase, Integer>() {
@Override
public Integer call(BriteDatabase briteDatabase) {
Cursor cursor = briteDatabase.query("SELECT * FROM Accounts");
try {
return cursor.getCount();
} finally {
cursor.close();
}
}
})
.flatMap(new Func1<Integer, Observable<Person>>() {
@Override
public Observable<Person> call(Integer count) {
if ( count == 0 ) {
return LoveToDo.basecampClient().fetchMe();
}
return null;
}
})
.map(new Func1<Person, Boolean>() {
@Override
public Boolean call(Person person) {
if ( person == null ) return false;
BriteDatabase database = LoveToDo.briteDatabase();
long count = database.insert(Account.TABLE, new Account.Builder()
.accountId(Settings.accountId)
.userName(Settings.userName)
.password(Settings.password)
.agent(Settings.agent)
.personId(person.id)
.build()
);
return count > 0;
}
})
.subscribeOn(Schedulers.io())
.observeOn( Schedulers.io() )
.subscribe();
Само собой разумеется, я не думаю, что это фантастический код. Что я хотел бы сделать, так это выяснить, как превратить этот код во что-то хорошее. Так что давайте использовать его и разобраться в его ужасности.
Во-первых, следует ли объединить операции вызова базы данных и веб-службы в одном операторе. Например:
Observable.just(LoveToDo.briteDatabase())
.flatMap(new Func1<BriteDatabase, Observable<Person>>() {
@Override
public Observable<Person> call(BriteDatabase briteDatabase) {
Cursor cursor = briteDatabase.query("SELECT * FROM Accounts");
int count;
try {
count = cursor.getCount();
} finally {
cursor.close();
}
if ( count == 0 ) {
return LoveToDo.basecampClient().fetchMe();
}
return null;
}
})
.map(new Func1<Person, Boolean>() {
@Override
public Boolean call(Person person) {
if ( person == null ) return false;
BriteDatabase database = LoveToDo.briteDatabase();
long count = database.insert(Account.TABLE, new Account.Builder()
.accountId(Settings.accountId)
.userName(Settings.userName)
.password(Settings.password)
.agent(Settings.agent)
.personId(person.id)
.build()
);
return count > 0;
}
})
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe();
Или есть веская причина держать такие операции изолированно в цепочке?
Второе, что меня беспокоит, это то, что это фоновая операция - ни один пользовательский интерфейс не будет обновляться напрямую в результате этого кода. Вот почему есть без параметров subscribe()
вызов функции. Но что происходит, когда есть исключение? Значит ли это, что мне придется сделать что-то вроде следующего?
.subscribe(new Action1<Boolean>() {
@Override
public void call(Boolean aBoolean) {
// Do nothing
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
// Do something with the exception
}
});
Кстати, мне нужно subscribeOn
когда observeOn
настроен на фоновый поток?
В-третьих, цепочка запускается с помощью наблюдателя SqlBrite. Позже в цепочке мне снова нужен SqlBrite, поэтому я получаю к нему доступ с помощью синглтона LoveToDo.briteDatabase()
, Это плохая идея? Есть лучший способ сделать это?
Наконец, есть ли способ break;
цепь? Было бы хорошо, если бы я мог отказаться от того, что я делаю, вместо того, чтобы проверять пропущенные данные на каждом шаге
1 ответ
Я вижу много вопросов.
- Каждый метод / оператор представляет собой "задачу", которая будет выполняться на основе предыдущих элементов и передавать элементы следующим операторам.
- Чтобы уменьшить детализацию кода, мы обычно используем Retrolambda или Gradle Retrolamda с RxJava. Если вы не хотите использовать Retolambda, вы можете создать класс
NameModel
который содержит всю логику от создания Observable вплоть до подписки (). Там есть вся необходимая логика, изолированная. - Это отличная идея всегда иметь
onError
Функция в подписке, если у вас есть сетевой вызов, если вы не перехватите все возможные ошибки где-то раньше, например, с помощью onErrorReturn. Ошибка onError поможет вам, если что-то пойдет не так, например, уведомить пользователя. Также рекомендуется обновлять что-то в подписке, а не из цепочки, таким образом изолируя контент оператора. subscribeOn
делает процесс на заднем плане, а неobserveOn
, Так что нет,observeOn
не требуется, если вы не измените поток, пример здесь.- Лучший способ "разорвать" цепочку - выдать ошибку или, что более сложно, отписать цепочку изнутри, используя пользовательский
.lift()
оператор с заказным абонентом.
Обновление на основе комментария:
Из 2 выше второго, но я бы предпочел что-то подобное:
Observable.just(LoveToDo.briteDatabase())
.flatMap(briteDatabase -> {
Cursor cursor = briteDatabase.query("SELECT * FROM Accounts");
int count;
try {
count = cursor.getCount();
} finally {
cursor.close();
}
if (count == 0) {
return LoveToDo.basecampClient().fetchMe()
.map(person -> insertPerson(person, briteDatabase));
}
// if you want to track the account creation
return just(false);
})
.subscribeOn(Schedulers.io())
.subscribe(personInserted -> {
// do something if the person was created or not
}, e -> {
});
private Boolean insertPerson(Person person, BriteDatabase briteDatabase) {
long count = briteDatabase.insert(Account.TABLE, new Account.Builder()
.accountId(Settings.accountId)
.userName(Settings.userName)
.password(Settings.password)
.agent(Settings.agent)
.personId(person.id)
.build());
return count > 0;
}