Иногда я в чужом коде встречаю использование 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, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня