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 на практическом примере