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

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

 

 

 

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 Action1<Integer>() {
           @Override
           public void call(Integer integer) {
               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 Action1<Integer>() {
           @Override
           public void call(Integer integer) {
               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 Action1<Integer>() {
               @Override
               public void call(Integer integer) {
                   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 Action1<Integer>() {
                   @Override
                   public void call(Integer integer) {
                       log("onNext " + integer);
                   }
               },
               new Action1<Throwable>() {
                   @Override
                   public void call(Throwable throwable) {
                       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

Система Orphus

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

 

Telegram канал



Android чат в Telegram



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



Страница в Facebook

Поддержка проекта

Яндекс
410011180491924

WebMoney
R248743991365
Z551306702056

Paypal

Яндекс.Метрика