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 ответ

Решение

Я вижу много вопросов.

  1. Каждый метод / оператор представляет собой "задачу", которая будет выполняться на основе предыдущих элементов и передавать элементы следующим операторам.
  2. Чтобы уменьшить детализацию кода, мы обычно используем Retrolambda или Gradle Retrolamda с RxJava. Если вы не хотите использовать Retolambda, вы можете создать класс NameModel который содержит всю логику от создания Observable вплоть до подписки (). Там есть вся необходимая логика, изолированная.
  3. Это отличная идея всегда иметь onError Функция в подписке, если у вас есть сетевой вызов, если вы не перехватите все возможные ошибки где-то раньше, например, с помощью onErrorReturn. Ошибка onError поможет вам, если что-то пойдет не так, например, уведомить пользователя. Также рекомендуется обновлять что-то в подписке, а не из цепочки, таким образом изолируя контент оператора.
  4. subscribeOn делает процесс на заднем плане, а не observeOn, Так что нет, observeOn не требуется, если вы не измените поток, пример здесь.
  5. Лучший способ "разорвать" цепочку - выдать ошибку или, что более сложно, отписать цепочку изнутри, используя пользовательский .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;
}
Другие вопросы по тегам