Иногда я в чужом коде встречаю использование just, чтобы вызвать какой-либо метод, используя RxJava. В этом посте я хотел бы рассказать, почему не стоит так делать, и показать правильный вариант реализации.

Более подробно о RxJava можно прочитать здесь

Чтобы использовать RxJava в проекте, добавьте следующие dependencies

    compile 'io.reactivex.rxjava2:rxjava:2.1.0'
    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'

  

 

 

just

Рассмотрим пример. У нас есть метод longAction, который нам надо вызывать асинхронно, т.к. он долго работает.

private int longAction(String text) {
   log("longAction");

   try {
       TimeUnit.SECONDS.sleep(1);
   } catch (InterruptedException e) {
       e.printStackTrace();
   }

   return Integer.parseInt(text);
}

Здесь я просто ставлю паузу в одну секунду и конвертирую строку в число.


Используем just, чтобы обернуть этот метод в RxJava

Observable.just(longAction("5"))
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Consumer<Integer>() {
            @Override
            public void accept(final Integer integer) throws Exception {
                log("onNext " + integer);
            }
        });

Мы используем subscribeOn, чтобы указать IO поток для выполнения метода, и observeOn, чтобы результат пришел к нам в UI потоке.

 

При запуске, лог покажет следующее:
0 longAction [main]
1000 onNext 5 [main]

Число в начале строки лога - это время в мсек., а в квадратных скобках указано имя потока.

Обратите внимание, что longAction выполнился в UI потоке. Если быть более точным, то just выполнится в том потоке, в котором вы выполняете метод subscribe. Часто это бывает UI поток. А значит любая долгая операция, выполненная в just, временно заблокирует ваш UI поток, что недопустимо. И subscribeOn вам здесь никак не поможет.

 

 

Callable

Вместо just используйте оператор fromCallable, который на вход ожидает Callable.

Создаем обертку Callable для метода longAction. CallableLongAction принимает строку и, при запуске, вызовет метод longAction.

class CallableLongAction implements Callable<Integer> {

   private final String data;

   public CallableLongAction(String data) {
       this.data = data;
   }

   @Override
   public Integer call() throws Exception {
       return longAction(data);
   }
}

 

Создаем Observable, используя CallableLongAction в операторе fromCallable

Observable.fromCallable(new CallableLongAction("5"))
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Consumer<Integer>() {
            @Override
            public void accept(final Integer integer) throws Exception {
                log("onNext " + integer);
            }
        });

Запускаем, смотрим лог:
0 longAction [RxIoScheduler-2]
1000 onNext 5 [main]

longAction выполнился в IO потоке. Теперь все корректно

 

 

 

Обработка ошибок

Кроме потоков, есть еще одна причина использовать Callable. Если ваш код может выбросить checked exception, то при использовании just - это будет выглядеть совсем грустно.

Давайте добавим throws IOException к методу longAction

private int longAction(String text) throws IOException {
   log("longAction");

   try {
       TimeUnit.SECONDS.sleep(1);
   } catch (InterruptedException e) {
       e.printStackTrace();
   }

   return Integer.parseInt(text);
}

 

Теперь при использовании just вам придется обернуть все в try catch:

try {
    Observable.just(longAction("5"))
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Consumer<Integer>() {
                @Override
                public void accept(final Integer integer) throws Exception {
                    log("onNext " + integer);
                }
            });
} catch (IOException e) {
    e.printStackTrace();
}

Иначе у вас просто не скомпилируется код.

И даже добавив обработчик ошибок onError в ваш Observable, вы все равно будете получать ошибку в catch. Это выглядит не очень удобно. Все ошибки должны приходить в onError.

 

С Callable таких проблем нет.

Observable.fromCallable(new CallableLongAction("5"))
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(
                new Consumer<Integer>() {
                    @Override
                    public void accept(final Integer integer) throws Exception {
                        log("onNext " + integer);
                    }
                },
                new Consumer<Throwable>() {
                    @Override
                    public void accept(final Throwable throwable) throws Exception {
                        log("onError " + throwable);
                    }
                });

Все ошибки из longAction вы будете получать в onError.

 

 

 

void метод

Если ваш долгий метод ничего не возвращает, то Callable для него будет выглядеть так:

Callable<Void> clb = new Callable<Void>() {
   @Override
   public Void call() throws Exception {
       longAction("5");
       return null;
   }
};

Completable.fromCallable(clb).subscribe(...);

 

 

Либо, можно использовать fromAction

Completable.fromAction(new Action() {
    @Override
    public void run() throws Exception {
        longAction("5");
    }
}).subscribe(...)

 

 

 

 

 


Присоединяйтесь к нам в Telegram:

- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование 

- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

- тут можно посмотреть, над какими уроками я сейчас работаю, и о чем будут следующие уроки 




Language

Социальные сети

 

Telegram канал



Android чат в Telegram



Группа ВКонтакте



Страница в Facebook