[Разработка мобильных приложений, Dart, Flutter] Моя история реализации офлайн приложения Хабра
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Создание своего приложения Хабра уже вошло в традицию среди хабрюзеров. Я решил не отставать и сделать своё. В данной статье я расскажу в первую очередь о том, как создавался клиент для Хабра, архитектурные и технические решения, их предпосылки и анализ, какие трудности были и в последнюю очередь о функционале приложения. Предыстория Все началось с предложения друга попробовать Flutter, а я оказался не против. Создавать приложение, но какое? На момент создания репозитория мне показалось, что мобильному клиенту Хабра крайне не хватает офлайн режима, собственно это и стало причиной и на этом строится идея всего приложения - смотреть статьи в офлайне и по возможности продолжить чтение с того момента где остановился и слегка настроить отображение текста под себя (задача минимум которую я постарался выполнить).До этого у меня был опыт пет проектов на React+TypeScript и VanilaJs, поэтому было интересно сравнить React и Flutter.В момент старта я предполагал, что html это легко и просто, а его отображение — это плевое дело, есть одно “но”: я захотел отображать html нативными виджетами, а не какими-то web-view. Вжух и проект усложнился, но не будем пока об этом. Выделяем слои Изначально я не предполагал чего-то сверх сложного. Да, об изолятах в Dart я слышал, но не думал, что буду их использовать (на что Flutter сказал: ты будешь использовать изоляты, т.к. тяжёлые вычисления, парсинг и сетевые запросы мешают ui потоку). На момент старта я выделил три слоя:
- ui
- habr-storage - к нему мы обращаемся за информацией, а он сам решает, идти ли в бд или обратится к апи хабра.
- habr-api - обычные запросы по http с обработкой полученных данных.
В принципе, это оказался не такой уж и плохой вариант. Архитектурно похоже на MVVM.
Тут я постарался схематично изобразить архитектуруUIПо API Хабр высылает html и иногда js для отображения красивых мегапостов. По этой причине перед мной встала задача отображение html и в идеале красиво. Для отображения html на Flutter, на сколько мне известно, есть два варианта:
- web-view
- нативные (для Flutter) виджеты
Я решил пренебречь первым вариантом, так как если бы хотел его использовать, писал приложение на React Native, а хотелось ощутить всю мощь нового фреймворка.
Основная лента с которой пользователь встречается при включении приложенияРендер HTML Это, на мой взгляд, самая большая проблема. Чтобы отображать некоторые элементы вроде iframe, нужно поизвращаться, либо использовать web-view. На данный момент я не очень доволен, но я просто выдаю пользователю ссылку, и он спокойно может посмотреть, что внутри. Это компромисс, но без этого никуда.Рендер я выполняю в три этапа:
- Парсинг
- Препроцессинг
- Рендер
Первый и второй этап я стараюсь делать единожды, т.к. они достаточно трудоёмкие.Парсинг html достаточно тривиальная задача, особенно если парсишь с помощью библиотеки, что я и сделал. А вот препроцессинг не самый очевидный этап. После того как библиотека прошлась и выдала распарсенный html, хочется просто взять корневой div, пройтись по всем нодам и отобразить. На мой взгляд, это достаточно проблемно, т.к. придется поддерживать множество элементов как самого html, так и комбинаций с css классами. К тому же некоторые авторы иногда любят добавлять изюминки к оформлению своих статей, из-за чего все это выглядит не очень, если отображать "как есть".Пример необычного html который приходится детектировать:
<div class="spoiler" role="button" tabindex="0">
<b class="spoiler_title">Заголовок спойлера</b>
<div class="spoiler_text">Контент спойлера</div>
</div>
Данный кусок часто встречается в статьях, как и тег details - приходится поддерживать обе версии. Препроцессинг проходит в 2 этапа:
- Конвертация html блоков в дерево смысловых элементов
- Оптимизация дерева
Идейно дерево смысловых элементов не особо отличается от html5. В нем есть основной набор высокоуровневых элементов, которые привычны для верстальщика:
- Изображение (с подписью)
- Абзац
- Заголовок
- Список (с разделением на упорядоченный/неупорядоченный)
- Таблица
- Код
- Iframe
- Цитата
- Спойлер
Комментарии Отображение комментариев - один из самых интересных этапов. Комментариев может быть много и если реализовать их отображение не оптимально, то все будет лагать или рендериться овердофига лет.Flutter для таких задач имеет аналог RecyclerView из мира Android, и называется он ListView. Каждый элемент он рендерит только когда он виден и не рендерит (и соответственно не тратит мощности аппарата), когда не виден.
Отображение комментариевИзначально я реализовал просто дерево комментариев, то есть у каждого комментария есть две части: контент и область дочерних элементов. От этой реализации я отказался из-за её нежизнеспособности - скрол лагает, т.к. дочерних элементов почти всегда много.
Первая версия с древовидным отображениемТекущая реализация, которой я вполне доволен - также отображаем дерево, но лишь один раз в памяти, трансформируем в массив нод-комментариев с известным уровнем вложенности, после строим по нему "плоское" отображение по вычисленной глубине. Получается, что ui выглядит также, но теперь ListView не обязан отображать все дочерние элементы разом, да и отобразить N отступов не проблема, особенно, когда N известно.
Текущая реализация с «плоским» отображениемБлагодаря такому решению приложение способно отображать дерево комментариев популярных статей, 1000 комментариев - не проблема, главное - скорость интернета.СтатьиИх отображением я пока не очень доволен. Дело в том, что при некотором количестве очень больших картинок видны лаги. При повседневном чтении обычно это не заметно, но бывают прожорливые статьи. В целом решение есть, и оно такое же, как с комментариями - использовать ListView и разбить статью на "плоские" ноды и отображать только те, которые видны. Работа над этим ведётся, но не спешно.
Habr-APIЯ обращаюсь напрямую по адресу мобильной версии m.habr.com/kek/v2/Из того что поддерживает мобильная версия хабра и не поддерживает приложение с точки зрения API:
- Авторизация и доступ к информации пользователя
- Хабы
В целом, тут нет ничего особо интересного, кроме моего небольшого возмущения, например, страницы основной ленты, если их не подгружать все разом, могут устареть, пока ты читаешь какой-то пост, и при подгрузке следующей страницы возникают дубликаты, либо посты теряются. Исправлять это приходится на клиенте, что так себе. Лично мое впечатление – API не самый удобный и построен так чтобы делать больше запросов чем нужно. Да, и в целом Хабр любит обновлять API из-за чего приходится обновлять приложение. Я думал о прокси сервере, чтобы он обрабатывал API Хабра и посылал данные в том формате, которое ожидает приложение. От такого решения я отказался из-за его непрозрачности по отношению к пользователям приложения.Habr-Storage На старте я начал использовать Moor (Room, но для Dart) совместно с SQLite, но на каком-то из этапов разработки вдруг понял, что в проекте уже две БД. Вторая – Hive, NoSQL Key-Value база данных, и она чертовски удобна. Когда подключал её как зависимость, чтобы хранить пару настроек приложения (темы и опции отображения текста), я и не представлял, как удобно ей будет пользоваться.В SQLite я храню html текст статьи, авторов (аватарка, никнейм и прочее) и изображения, остальное лежит в Hive. Отдельно хочу рассказать про изображения. Изображения я храню лишь частично в SQLite. Сами изображения я храню на файловой системе. В базе хранится url, по которому запрашивается изображение, и path файловой системы. Path я формирую как хеш-код url`а и конкатенирую с текущим временем вставки в миллисекундах. Примерно вот так:
path = str(hash(url)) + str(datetime.now().millisecondsSinceEpoch)
IsolatesПо своей природе Dart однопоточный язык, но однопотоком сыт не будешь, поэтому есть возможность работы нескольких потоков, работают они схоже с web-workers и называются Isolate. Обычно их стоит использовать в любой необычной ситуации, вроде HTTP запросов, парсинга JSON, сложные вычисления, так вы уменьшите количество подвисаний вашего приложения на Flutter. Для организации вычислений вне основного потока вы можете использовать отдельный изолят с какой-то продуманной логикой общения с другими потоками, либо организовать пул потоков-изолятов.Примечание: Flutter предоставляет возможность быстро запустить изолят выполнить нужную функцию и закрыть изолят. Метод называется compute. Я пришел к компромиссу – выделил основные операции, которые занимают достаточно много времени, и создал для каждой операции отдельные воркеры. Преимущества данного подхода в двух вещах:
- Простота. Она как у метода compute, но при этом производительность на уровне пула потоков, иногда чуть ниже, иногда чуть выше, тут зависит от реализации пула потоков.
- Разные операции не мешают друг другу. Гипотетически есть вероятность, что у двух задач может быть одинаковый приоритет, например, middle, но при этом одна из них достаточно долгая, а вторая короткая, и в случае если нам важно, чтобы короткая не дожидалась долгой задачи, пул потоков придется подстраивать ради баланса. Отдельные воркеры в этом плане на мой взгляд удобнее.
Следующие операции я выделил в отдельные воркеры:
- Хеширование
- Загрузка изображений
- Подсветка синтаксиса
Для операции парсинг и препроцессинг пока использую метод compute.ЗаключениеНа этом в принципе все, что я хотел бы рассказать о внутреннем устройстве приложения. Данный пост я не задумывал как рекламу, но желание потыкать приложение и посмотреть код может возникнуть, поэтому ссылку на github прикладываю.А дальше пара скриншотов приложения:
===========
Источник:
habr.com
===========
Похожие новости:
- [Разработка мобильных приложений, Amazon Web Services, Microsoft Azure, Google Cloud Platform, Serverless] Молния: предстоящие вебинары про Serverless технологии
- [Разработка мобильных приложений, Машинное обучение, Софт, Искусственный интеллект, Flutter] Flitter Your Business With AI Integrated Flutter App Development
- [Программирование, Разработка мобильных приложений, Производство и разработка электроники, Гаджеты, Интернет вещей] Русские программисты не сдаются-2
- [Разработка мобильных приложений, Разработка под Android] Фоновая работа в Android: обзор возможностей WorkManager
- [Разработка мобильных приложений, Разработка под Android] Навигация в многомодульном приложении на Jetpack без магии и DI
- [Разработка мобильных приложений, Монетизация мобильных приложений, Медийная реклама, Управление продуктом, IT-компании] Facebook всё-таки изменит свои рекламные инструменты из-за новых правил Apple о приватности
- [Разработка мобильных приложений, Разработка под e-commerce, Карьера в IT-индустрии, IT-компании] «Кадровый голод» или почему IT-компании находятся в постоянном поиске сотрудников
- [Разработка под iOS, Разработка мобильных приложений, Разработка под Android] В ВТБ Онлайн появилась темная тема
- [Разработка веб-сайтов, Разработка мобильных приложений, Развитие стартапа, Дизайн, Удалённая работа] Иной подход к коммуникации удаленных команд
- [Dart, Flutter] Flutter meetup: речевая аналитика, отрисовка оригинальных интерфейсов и код для перехода к null-safety
Теги для поиска: #_razrabotka_mobilnyh_prilozhenij (Разработка мобильных приложений), #_dart, #_flutter, #_mobilnoe_prilozhenie (мобильное приложение), #_flutter, #_habr_app, #_habr.com, #_razrabotka_mobilnyh_prilozhenij (
Разработка мобильных приложений
), #_dart, #_flutter
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 15:35
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Создание своего приложения Хабра уже вошло в традицию среди хабрюзеров. Я решил не отставать и сделать своё. В данной статье я расскажу в первую очередь о том, как создавался клиент для Хабра, архитектурные и технические решения, их предпосылки и анализ, какие трудности были и в последнюю очередь о функционале приложения. Предыстория Все началось с предложения друга попробовать Flutter, а я оказался не против. Создавать приложение, но какое? На момент создания репозитория мне показалось, что мобильному клиенту Хабра крайне не хватает офлайн режима, собственно это и стало причиной и на этом строится идея всего приложения - смотреть статьи в офлайне и по возможности продолжить чтение с того момента где остановился и слегка настроить отображение текста под себя (задача минимум которую я постарался выполнить).До этого у меня был опыт пет проектов на React+TypeScript и VanilaJs, поэтому было интересно сравнить React и Flutter.В момент старта я предполагал, что html это легко и просто, а его отображение — это плевое дело, есть одно “но”: я захотел отображать html нативными виджетами, а не какими-то web-view. Вжух и проект усложнился, но не будем пока об этом. Выделяем слои Изначально я не предполагал чего-то сверх сложного. Да, об изолятах в Dart я слышал, но не думал, что буду их использовать (на что Flutter сказал: ты будешь использовать изоляты, т.к. тяжёлые вычисления, парсинг и сетевые запросы мешают ui потоку). На момент старта я выделил три слоя:
Тут я постарался схематично изобразить архитектуруUIПо API Хабр высылает html и иногда js для отображения красивых мегапостов. По этой причине перед мной встала задача отображение html и в идеале красиво. Для отображения html на Flutter, на сколько мне известно, есть два варианта:
Основная лента с которой пользователь встречается при включении приложенияРендер HTML Это, на мой взгляд, самая большая проблема. Чтобы отображать некоторые элементы вроде iframe, нужно поизвращаться, либо использовать web-view. На данный момент я не очень доволен, но я просто выдаю пользователю ссылку, и он спокойно может посмотреть, что внутри. Это компромисс, но без этого никуда.Рендер я выполняю в три этапа:
<div class="spoiler" role="button" tabindex="0">
<b class="spoiler_title">Заголовок спойлера</b> <div class="spoiler_text">Контент спойлера</div> </div>
Отображение комментариевИзначально я реализовал просто дерево комментариев, то есть у каждого комментария есть две части: контент и область дочерних элементов. От этой реализации я отказался из-за её нежизнеспособности - скрол лагает, т.к. дочерних элементов почти всегда много. Первая версия с древовидным отображениемТекущая реализация, которой я вполне доволен - также отображаем дерево, но лишь один раз в памяти, трансформируем в массив нод-комментариев с известным уровнем вложенности, после строим по нему "плоское" отображение по вычисленной глубине. Получается, что ui выглядит также, но теперь ListView не обязан отображать все дочерние элементы разом, да и отобразить N отступов не проблема, особенно, когда N известно. Текущая реализация с «плоским» отображениемБлагодаря такому решению приложение способно отображать дерево комментариев популярных статей, 1000 комментариев - не проблема, главное - скорость интернета.СтатьиИх отображением я пока не очень доволен. Дело в том, что при некотором количестве очень больших картинок видны лаги. При повседневном чтении обычно это не заметно, но бывают прожорливые статьи. В целом решение есть, и оно такое же, как с комментариями - использовать ListView и разбить статью на "плоские" ноды и отображать только те, которые видны. Работа над этим ведётся, но не спешно. Habr-APIЯ обращаюсь напрямую по адресу мобильной версии m.habr.com/kek/v2/Из того что поддерживает мобильная версия хабра и не поддерживает приложение с точки зрения API:
path = str(hash(url)) + str(datetime.now().millisecondsSinceEpoch)
=========== Источник: habr.com =========== Похожие новости:
Разработка мобильных приложений ), #_dart, #_flutter |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 15:35
Часовой пояс: UTC + 5