Comments 23
Подключаем поддержку лямбда-выражений — используем новые возможности языка Java 8 на платформе Android N. Чтобы использовать возможности языка Java 8 также необходимо подключить и новый компилятор Jack, для чего добавьте в файл build.gradle
Доступны ли лямбды на предыдущих версиях? Или придется использовать retrolambda?
Я бы не рекомендовал новичкам учиться по этой статье, даже если забыть, что есть Retrofit, и рассматривать это как просто как базовый пример.
Масса недочетов, плохой код-стайл и беспорядок в терминах и понятиях.
Масса недочетов, плохой код-стайл и беспорядок в терминах и понятиях.
А можно конкретизировать ваши замечания? Доработаю статью.
Я не силён в Android, но:
1) getURLs и getTitles у вас могут null вернуть. Тогда все методы в
2) Не уверен, что Jsoup нормально хендлит повороты экрана и т.п. вещи связанные с жизненным циклом Android приложений.
1) getURLs и getTitles у вас могут null вернуть. Тогда все методы в
MainExample.java
, которые вызывают queryURLs
, при попытке прогнать полученный список String url: urls
выбросят исключение.2) Не уверен, что Jsoup нормально хендлит повороты экрана и т.п. вещи связанные с жизненным циклом Android приложений.
1) getURLs и getTitles у вас могут null вернуть. Тогда все методы в MainExample.java, которые вызывают queryURLs, при попытке прогнать полученный список String url: urls выбросят исключение.
Может быть не совсем красиво получилось, но здесь специально сделал возврат null, чтобы потом показать использование .filter(title -> title != null)
2) Не уверен, что Jsoup нормально хендлит повороты экрана и т.п. вещи связанные с жизненным циклом Android приложений.
Jsoup никак не связан с поворотом экрана, в данном простом примере при повороте экрана происходит новая загрузка данных.
Как уже написали выше, у вас проблемы с методами getURLs и getTitles. Следовало бы поймать exception внутри Observable и вернуть его подписчику в onError, чтобы он решил, что с ним делать.
— Observable queryURLs(String url) — строка объявляет метод, который порождает строку ссылки на сайт для парсинга и возвращающего список ссылок.
Если уж решили зачем-то вдаваться в такие мелочи, выражайтесь корректно.
— new Observable.OnSubscribe() — интерфейс OnSubscribe создает подписчика
Интерфейс OnSubscribe ничего не создает, это просто интерфейс с 1 методом, который вызовется при подписке.
— subscribeOn(Schedulers.io()) — метод subscribeOn запускает наш код в дополнительном потоке;
Не совсем так. Этот метод подписывает всех Observable выше по цепочке на определенный планировщик. Повторный вызов метода ниже по цепочке (с другим планировщиком) не даст никакого результата, например.
Названия методов Example0...1 и т.д. с заглавной буквы.
Статьи в списке источников куда полезнее.
Добавил бы еще это, Rx далеко не заканчивается на сетевых запросах:
Пагинация 1
Пагинация 2
Shake detector
Доклад Артема Зинатулина
И просто десятки статей всяких блоггеров, не знаю почему вдруг «хороших материалов мало»
— Observable queryURLs(String url) — строка объявляет метод, который порождает строку ссылки на сайт для парсинга и возвращающего список ссылок.
Если уж решили зачем-то вдаваться в такие мелочи, выражайтесь корректно.
— new Observable.OnSubscribe() — интерфейс OnSubscribe создает подписчика
Интерфейс OnSubscribe ничего не создает, это просто интерфейс с 1 методом, который вызовется при подписке.
— subscribeOn(Schedulers.io()) — метод subscribeOn запускает наш код в дополнительном потоке;
Не совсем так. Этот метод подписывает всех Observable выше по цепочке на определенный планировщик. Повторный вызов метода ниже по цепочке (с другим планировщиком) не даст никакого результата, например.
Названия методов Example0...1 и т.д. с заглавной буквы.
Статьи в списке источников куда полезнее.
Добавил бы еще это, Rx далеко не заканчивается на сетевых запросах:
Пагинация 1
Пагинация 2
Shake detector
Доклад Артема Зинатулина
И просто десятки статей всяких блоггеров, не знаю почему вдруг «хороших материалов мало»
И да, так себе идея создавать Observable, который сразу подписан на какие-то планировщики —
На каком планировщике обработать результат, пусть решает сам подписчик.
Кроме того, у вас все операторы под queryURLs(...) выполняются на планировщике с Looper'ом, то есть каждый из них постит результат в очередь событий. Хотя можно было бы это сделать только для метода subscribe.
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
На каком планировщике обработать результат, пусть решает сам подписчик.
Кроме того, у вас все операторы под queryURLs(...) выполняются на планировщике с Looper'ом, то есть каждый из них постит результат в очередь событий. Хотя можно было бы это сделать только для метода subscribe.
Как уже написали выше, у вас проблемы с методами getURLs и getTitles. Следовало бы поймать exception внутри Observable и вернуть его подписчику в onError, чтобы он решил, что с ним делать.
Придумал может не совсем корректный пример с возвратом null, чтобы потом показать использование .filter(title -> title != null)
А exception решил пока не обрабатывать по аналогии статьи “Грокаем* RxJava, часть первая: основы”, чтобы упростить пример.
— Observable queryURLs(String url) — строка объявляет метод, который порождает строку ссылки на сайт для парсинга и возвращающего список ссылок.
Если уж решили зачем-то вдаваться в такие мелочи, выражайтесь корректно.
Переделал:
— строка объявляет Observable метод, который принимает в виде входного параметра ссылку на сайт для парсинга и возвращает результат парсинга в виде списка ссылок <List> с указанного сайта.
— new Observable.OnSubscribe() — интерфейс OnSubscribe создает подписчика
Интерфейс OnSubscribe ничего не создает, это просто интерфейс с 1 методом, который вызовется при подписке.
Переделал:
— строка объявляет интерфейс OnSubscribe с одним методом (см. ниже), который вызовется при подписке.
— subscribeOn(Schedulers.io()) — метод subscribeOn запускает наш код в дополнительном потоке;
Не совсем так. Этот метод подписывает всех Observable выше по цепочке на определенный планировщик. Повторный вызов метода ниже по цепочке (с другим планировщиком) не даст никакого результата, например.
Переделал:
— метод subscribeOn подписывает всех Observable выше по цепочке на планировщик Schedulers.io().
Названия методов Example0...1 и т.д. с заглавной буквы.
Сам не понимаю, почему назвал с заглавной буквы и почему потом не бросилось в глаза. Все соответственно исправил.
Статьи в списке источников куда полезнее.
Добавил бы еще это, Rx далеко не заканчивается на сетевых запросах:
Пагинация 1
Пагинация 2
Shake detector
Доклад Артема Зинатулина
Огромное спасибо за ссылки на статьи, большинство из них мне не попались при поиске.
Было бы не плохо увидеть комментарии профессионалов с конкретными замечаниями и пожеланиями по доработке и развитию статьи. Хочется создать полезный материал для ИТ-сообщества. Тема не простая и хороших материалов мало.
Нужно стараться делать все вызовы pure.
Оператор map модифицирующий внешний контекст или захватывающий textView неприемлем.
Если где — то кроме subscribe есть closure, значит, что-то пошло не так, нужно пересматривать решение.
Зачем .flatMap(Observable::from)? Ради того, чтобы взять 7 первых ссылок?
queryTitle должен принимать список url и возвращать список title и не нужен будет flatMap, не нужно будет проверять на null.
Вы отсеиваете уже проделанную работу queryTitle, на которую было потрачено процессорное время,
если вы хотите брать 7 первых ссылок то надо фильтровать их на входе, или передавать ограничение в качестве параметра queryTitle.
subscribeOn и observeOn лучше оставить на усмотрение вызывающей стороне, так она сможет решить сама где она хочет производить парсинг, а где обновление UI.
В крайнем случае subscribeOn можно вызвать внутри реализации api, если ваш контракт подразумевает только асинхронное выполнение.
Что — то вроде того:
У вас же, queryURLs делает работу на background потоке и потом пушит результат в ui, а следом queryTitle опять начинает выполнять загрузку и парсинг в своем background потоке.
Это приводит к ненужным пушам в looper ui потока, которому и так есть чем заняться.
Поэтому лучше оставлять такие решения вызывающей стороне.
Надеюсь не очень скомкано получилось.
Оператор map модифицирующий внешний контекст или захватывающий textView неприемлем.
Если где — то кроме subscribe есть closure, значит, что-то пошло не так, нужно пересматривать решение.
Зачем .flatMap(Observable::from)? Ради того, чтобы взять 7 первых ссылок?
queryTitle должен принимать список url и возвращать список title и не нужен будет flatMap, не нужно будет проверять на null.
Вы отсеиваете уже проделанную работу queryTitle, на которую было потрачено процессорное время,
если вы хотите брать 7 первых ссылок то надо фильтровать их на входе, или передавать ограничение в качестве параметра queryTitle.
subscribeOn и observeOn лучше оставить на усмотрение вызывающей стороне, так она сможет решить сама где она хочет производить парсинг, а где обновление UI.
В крайнем случае subscribeOn можно вызвать внутри реализации api, если ваш контракт подразумевает только асинхронное выполнение.
Что — то вроде того:
queryURLs(url)
.map(this::queryTitle)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(titles -> textView.setText(Joiner.on("\n\n").join(titles));
У вас же, queryURLs делает работу на background потоке и потом пушит результат в ui, а следом queryTitle опять начинает выполнять загрузку и парсинг в своем background потоке.
Это приводит к ненужным пушам в looper ui потока, которому и так есть чем заняться.
Поэтому лучше оставлять такие решения вызывающей стороне.
Надеюсь не очень скомкано получилось.
Пожалуйста, хватит рекомендовать использовать Observable.create()
. Вы не делаете поддержку backpressure, не проверяете, что подписчик уже отписался и не обрабатываете ошибки (хотя тут RxJava спасет вас сама, но тем не менее).
Используйте Observable.fromAsync() если вам нужно конвертнуть callback api в реактивный (пока не рекомендую для библиотек, но рекомендую для приложений, api еще не стабильный).
Как же нелепо выглядит rx на джаве 7.
На джаве 8 или с retrolambda, rx начинает выглядеть нелепо, когда нужно добавить свой кастомный оператор.
Когда android разработчики начнут использовать Kotlin?
На джаве 8 или с retrolambda, rx начинает выглядеть нелепо, когда нужно добавить свой кастомный оператор.
Когда android разработчики начнут использовать Kotlin?
А зачем? Ретролямбы/лямбдыJava8 вполне себе справляются с чистотой кода. А переходить на новый язык ради «модного веяния» — это для вейперов.
Ну, на мой взгляд, он просто лучше. Если вы переживаете насколько он production ready в его защиту могу сказать — половина андройд студии написана на нем, a в 3 версии gradle будет его использовать в качестве скриптового языка вместо groovy.
Что касается rx, Java 8 и retro справляются ровно до тех пор пока вы используете стандартные операторы rx — сказывается отсутствие extension методов. Если вы хотите покинуть rx парадигму или наоборот в нее перейти, также возникают проблемы.
Пока все хорошо:
Но, если нам нужны свои кастомные операторы:
Вместо:
На самом деле это далеко не все. Чего стоят inline лямбды не напрягающие GC. И много чего еще…
Основная мысль, которую я пытаюсь донести, android — frontend разработка, текущая тенденция — тонкий, легкий клиент. Мы не пишем супернагруженные суперпараллельные вундеркластеры, так почему бы не писать наш тонкий (как правило) клиент лаконично и красиво.
Что касается rx, Java 8 и retro справляются ровно до тех пор пока вы используете стандартные операторы rx — сказывается отсутствие extension методов. Если вы хотите покинуть rx парадигму или наоборот в нее перейти, также возникают проблемы.
Пока все хорошо:
observable.map(x->x.getProp()).distinct();
Но, если нам нужны свои кастомные операторы:
ObservableOperators.anotherOperator(ObservableOperators.custom(observable.map(x->x.getProp()).distinct()), 10)
Вместо:
observable.map(x->x.getProp()).distinct().custom().anotherOperator(10);
На самом деле это далеко не все. Чего стоят inline лямбды не напрягающие GC. И много чего еще…
Основная мысль, которую я пытаюсь донести, android — frontend разработка, текущая тенденция — тонкий, легкий клиент. Мы не пишем супернагруженные суперпараллельные вундеркластеры, так почему бы не писать наш тонкий (как правило) клиент лаконично и красиво.
Sign up to leave a comment.
Основы реактивного программирования под Android на практическом примере