Pull to refresh

Comments 23

Стандартные Loaders проигрывают лишь в том, что не стали мэйнстримом и их не хвалит Jake Warton ;)
Стандартные Loaders проигрывают тем, что у них нет такого большого количества операторов/фич, как у RxJava :-)
человек передвинул карту, то предыдущий запрос теряет актуальность и мы должны запросить новые точки.

Конечно же это не так :)
Потому что это карты, и совершенно нормальные две ситуации — человек сдвинул карту не очень сильно(и большая часть старых данных валидна), и человек сдвинул карту обратно (и надо бы использовать те данные что есть).
Это приводит к тому, что при сдвиге карты надо загрузить только дельту по сдвигу, дополнив, а не заменив, данные.
В общем в JS API этот момент немного самим API покрыт, под андроидом судя по всему — нет.

Так уж получилось — но Яндекс.Недвижка ну совсем не умеет работать с данными карты. А принцип там очень простой — при сдвиге карты данные (в уже видимой области) НЕ должны меняться.
Вы пишете про разные вещи, цитата относится к запросу, который ещё не успел выполниться на момент, когда подвинули карту и начали запрашивать точки из новой области.

Далее, чтобы избежать моргания пинов на карте, берётся пересечение множества уже загруженных точек с теми, которые только что получили. Таким образом, пины, которые вышли из области видимости, удаляются, которые появились — добавляются на карту.

А идея с загрузкой дельты вводит дополнительную сложность, поскольку надо считать эту дельту, объединять её результаты с текущими, это что касается клиента. С точки зрения сервера, не факт, что поиск в дельте будет быстрее, чем во всей области + с кластеризацией сложнее.
Вот в том и дело — что дополнительную сложность не вводит, новые пины у вас появляются рядом со старыми, да и кластеризация при таких условиях может полностью менять картинку при малейших изменений данных.
Вот смотрели вы на краешек Москвы, и все данные были там. Потому что их много.
А потом подвинули на пиксель — и картинка полностью изменилась. Потому что данные обычно запрашивают с никим лимитом, и раньше этот лимит «тратился» на центр города.
У меня (к счастью? к сожалению?) нет возможности проверить как работает андроид приложение, но в вебе сейчас все с этим плохо, и всегда так и было – информация приходит не равномерно.
Сейчас есть два «хороших» решения проблемы — или загрузка данных дискретными «тайлами». В том числе там нет понятия «запрос, который еще не успел выполнится» — тайл в любом случае может оставаться актуальным, и пускай продолжает загружаться. Либо ограничения лимита через серверную кластеризацию, по тем же Z-кодам.
PS: Z-code, он же Morton code, может быть заменен Geocode или hilbert code. В общем 1D spatial index.

Опять же — если хранить данные в Z кодах, а запрашивать в тайлах — запрос превращается в поиск в 1D интервале и начинает работать чуть чуть быстрее.
У меня нет ссылки на «не матан», а на понятное обьяснение задачи, но есть видео.

Ну и конечно же все эти мозги без особых проблем спрячутся за фасадом функции getClusters, а сам пример с RxJS очень понравился.
В таком случае, я советую вам сначала посмотреть Андроид приложение, потому что карта там отличается от десктопной. И пины там ведут себя как я описал выше.
Гифка кратко, но показывает как это выглядит.
При горизонтальном драге перестроения быть не должно. На гифке есть «хорошая» догрузка при драге направо, и «плохая» при драге обратно. В том числе кластеризация не «устойчивая».
Когда я похожую проблему решал много лет назад для схожего по тематике сайта gdeetotdom – мне первое решение завернули сразу именно из-за «раздражающих анимашек» неустойчивой кластеризации.

Да, увидел о чём вы. Мне кажется это единичный случай, потому что я не могу это воспроизвести.

На более не равномерных данных, например на обзорных масштабах, когда виден и город (густо) и область(пусто), или когда количество обьявлений упрется в лимит — эффект будет более заметен.
В обучающем материале, думаю, стоит упомянуть, что использование Observable.create считается продвинутой и нерекомендованной практикой, из-за возможности легко наделать себе кучу проблем. Для случая, описанного в статье, лучше использовать fromEmitter

Цитата <a https://github.com/ReactiveX/RxJava/wiki/Creating-Observables">отсюда
create( ) — advanced use only! create an Observable from scratch by means of a function, consider fromEmitter instead
fromEmitter() — create safe, backpressure-enabled, unsubscription-supporting Observable via a function and push events.


Буквально на неделе в чате полдня выясняли, почему у человека после возникновения ошибки половина цепочки вызовов продолжала работать (до первого flatMap, как потом выяснилось), а другая половина нет. Также не работал retry. Причиной оказалось некорректное использование Observable.create, сначала был пропущена а потом не туда вставлена проверка subscriber.isUnsubscribed.

Согласен, fromEmitter выглядит проще и надёжнее.
Сам его до этого не использовал, потому что в тех редких местах где использовал Observable.create проблем с backpressure не было. А в других использовал Observable.SyncOnSubscribe и Observable.AsyncOnSubscribe.

UFO just landed and posted this here

ИМХО, fromEmitter для более простых случаев. AsynOnSubscribe упрощает написание собственного источника, поддерживающего Backpressure и умеющего выдавать запрашиваемое количество элементов.

А как насчет AsyncOnSubscribe? Юзали?


К сожалению, не доводилось попробовать.

Можно ли было внутри FilterHolder'a использовать BehaviorSubject вместо PublishSubject'а и сохранения последнего элемента последовательности "вручную"?

В принципе можно, поменяв как работает observeChanges. У нас ещё есть синхронное получение значения, поэтому сделали так по привычке (хотя сейчас посмотрел, что BehaviorSubject имеет метод getValue).

Синхронное получение значения можно выполнить через .getValue() у subject'a
AndroidSchedulers.mainThread() использует Message.sendDelayed внутри, что может привести к NPE, когда получателя события уже нет, о чем говорит в своем докладе инженер из Square. Вы с такой проблемой не сталкивались? Если да, то как решали?

Данный доклад абсолютно очень misleading.


В чем проблема Message.sendDelayed()? Как будто у нас есть другие способы передать управление в main поток из других потоков. От того, что они передвинули переход в main поток чуть повыше в бизнес логике, смысл не поменялся, в каком-то месте идет передача управления из какого-то потока через Handler и отрисовка данных. Не хотите NPE — не делайте рейс кондишенов :)


К NPE вы можете прийти только если вы сами обнулите ссылки, с которыми работаете в коллбеках, до того как отпишитесь от Subscription, при таком коде у вас будет рейс кондишен. То есть это чисто логическая ошибка при написании кода, а не "подарок" от Android Framework.


Магии никакой нет, GC/VM за вас ссылки не обнуляет, если хотите какие-то ссылки обнулить руками (в 99% это ошибка в дизайне, тк у нас рантайм с GC), то делайте это вместе с subscription.unsubscribe() (порядок не важен, тк как правило, вы делаете это в том же потоке, в котором потом будет работать коллбек, но если сложно про это думать, то чуть ниже написано более общее правило).


Железобетонное правило: делайте subscription.unsubscribe() как можно раньше (до обнулления ссылок, etc), тогда проблем не будет.

Sign up to leave a comment.