Warning: Undefined array key 0 in /var/www/tgoop/function.php on line 65

Warning: Trying to access array offset on value of type null in /var/www/tgoop/function.php on line 65
26 - Telegram Web
Telegram Web
Сегодня буду проводить публичное собеседование на YouTube канале Android Broadcast. Мы будем проектировать мультиплатформееное приложение и обсуждать Kotlin Multiplatform, Coroutines, Compose. Это уже третье по счету (раз, два) публичное собеседование в котором я принимаю участие и так как корутины и compose мы обмусолили со всех сторон, сегодня больше сосредоточимся на KMP и на практических навыках написания мультиплатформенных приложений.

Начинаем прямой эфир в 19:00 по мск, так что залетайте на трансляцию.

После трансляции, пишите комменты под этим постом👇, если вы считаете, что что-то осталось не раскрытым или просто хотите предъявить мне за то, что я плохо провожу собесы, в общем не стесняйтесь)
Combine vs flatMapLatest + map

По горячим следам вчерашнего собеса, осталось недопонимание в чем разница между этими операторами.
Для решения задачки действительно можно было бы использовать оба решения, но есть ли разница?

В случае использования flatMapLatest за место combine:
👉 Будем каждый раз при изменении токена, пересоздавать счетчик. Это не критично, но если переставить местами два flow, то будем уже каждый раз ходить в БД, и это уже плохо
👉 Если за место flatMapLatest использовать любой другой flatMap*, то получим некорректное поведение, так так не отменим ticker flow
👉 Так как flatMapLatest отменяет корутину, важно, чтобы ticker поддерживал кооперативную отмену, иначе снова получим некорректное поведение

Чтобы запомнить все разнообразие операторов и как они работают, очень советую интерактивные marble диаграммы. Этот сайт про RxJs, но на самом деле разницы особо нет, если вам каких-то операторов не хватает во Flow, то можно воспользоваться библиотекой FlowExt

#Coroutines
Какой оператор во Flow представлен на marble диаграмме ниже?
Anonymous Quiz
4%
flatMapLatest
19%
combine
52%
zip
24%
flatMapMerge
Что выбрать для навигации в Compose🤨

Это довольно распространенный вопрос и на сегодняшний день выбор либ просто огромен на любой вкус и цвет. Так что же выбрать? Конечно же Decompose решать вам на основе требований к навигации в вашем приложении.

🤖 Jetpack Compose Navigation — официальная библиотека
👍 Поддержка от Google, интеграция с ViewModel
👎Только для Android и еще миллион минусов

🤖 Jetpack Compose Destinations — обертка над официальной либой
👍 Решает некоторые проблемы первой либы
👎 Добавляет новых проблем из-за кодогенерации и зависимости на accompanist либы

🤖 Modo — либа от создателя Cicerone, Константина Цховребова
👍 Строится на принципах UDF, очень простая
👎 Только для Android, еще не в релизе, маленькое коммьюнити

👩‍💻 Appyx — решение для навигации от Bumble
👍 Декларативный подход, классные анимации из коробки, поддержка KMP
👎 Только для Compose, довольно сложная, небольшое коммьюнити

👩‍💻 Voyager — популярная и простая либа для навигации
👍 Много интеграций с привычными инструментами, легкая в использовании, поддержка KMP
👎 Только для Compose, есть проблемы со стабильностью

🌳 Decompose — либа от Аркадия Иванова, автора MVIKotlin
👍 Единственное решение не завязанное на UI фреймворк, декларативный подход, огромная гибкость, высокая стабильность
👎 Высокий порог входа, приходится писать много кода

Есть еще решение Odyssey от Алексея Гладкова, но автор объявил о прекращении поддержки данной либы.

Таким образом настоятельно не рекомендую использовать официальное решение для навигации в любых более менее сложных приложениях. Можете посмотреть мой доклад, где я сравниваю эту либу и Decompose. Другие либы можно смело у себя использовать, но если вы проникнетесь подходом к навигации в Decompose, то можете посмотреть другой доклад, где уже разбираю как интегрироваться не только с Compose, но и SwiftUI.

Как вы поняли, я очень топлю за Decompose, хотя его сложность может многих отпугнуть, но стоит только проникнуться компонентым подходом и уже по-другому приложения писать не захочется!

#Compose #Navigation
Please open Telegram to view this post
VIEW IN TELEGRAM
R8 full mode

Не так давно обновил проект до Gradle 8 и получил краш в релизной сборке⚪️. Все из-за включенного по умолчанию R8 full mode. Прежде чем разберемся, что поменялось, давайте для начала вспомним, кто такой этот ваш R8.

☢️ R8 — это утилита для удаления лишнего кода, его минификации и оптимизации

Как работает
⚙️ Строит граф от рутов, помеченных -keep в правилах proguard, и удаляет все до чего не смог дотянуться

Причем тут proguard
⚙️ Ранее в Android использовался аналог R8 под названием ProGuard, правила остались для совместимости

Когда запускается
⚙️ Во время сборки с включенным флагом isMinifyEnabled

Где может стрельнуть
⚙️ При использовании рефлексии или JNI

Что за full mode
⚙️ Включает более агрессивный режим и вырезает еще больше кода. Например, классы, создаваемые только через рефлексию, должны явно помечаться через -keep правило. Также R8 удаляет сигнатуру дженериков, что стрельнуло у меня в связке Retrofit + RxJava2

❗️Вообще хорошей практикой считается то, когда либа уже содержит необходимые правила для R8 и вам не нужно об этом задумываться, но так бывает не всегда. Например, GSON только с последней версии стал включать правила по-умолчанию, но и это работает не для всех кейсов.

📌 Подробнее почитать про R8 full mode и известные проблемы можно тут, но эти правила мне не помогли, поэтому в комментах напишу, что помогло.
Также если хотите глубже погрузиться в правила ProGuard, то рекомендую официальный мануал и андроидовскую доку.

Столкнулись ли вы с подобной проблемой на своем проекте

#Android #R8 #ProGuard
Please open Telegram to view this post
VIEW IN TELEGRAM
Этим летом проводили MobileUpdate в Екатеринбуржском офисе Контура☁️, где было 5 крутых докладов по мобильной разработке. А теперь все записи докладов стали доступны на YouTube🟥

🤖 Влада Шамшукаева рассказала как работать с графикой в Compose, а также о том как одну и ту же задачу можно решать совершенно разными способами

🍏 Алексей Агапов набросил, что классы и паттерны проектирования вам больше не нужны, достаточно лишь функции и знания о Functional Core / Imperative Shell.

🤖 Игорь Гордеев поведал о том как уменьшать шаблонный код с помощью KSP на примере библиотеки VisualFSM

🍏 Анастасия Чупова поделилась довольно забавной историей о фейлах при работе с диплинками в SwiftUI

🤖 И наконец Евгений Мельцайкин рассказал про плагины в Gradle, как с помощью них сократить код в ваших gradle файлах и как при этом не выстрелить себе в ногу.

Я выступал в роли программного комитета и помогал ребятам с прогонами. Все докладчики постарались на славу и определенно заслуживают ваш лайк👍

P.S. Уровень монтажа и картинки просто мое почтение

#Video #iOS #Android
Please open Telegram to view this post
VIEW IN TELEGRAM
Декларативный Bottom Sheet

На мой взгляд, самый неудачный компонент в Compose из Material 2 — это Bottom Sheet. Он долгое время крашился при изменении конфигурации, его кучу раз переписывали, но все-равно на сегодняшний день он содержит много проблем:
😀 Приходится пилить костыли для работы с WindowInsets
😀 Он не прилипает к низу экрана
😀 Производительность оставляет желать лучшего
😀 Скрытие Scrim нельзя кастомизировать

А самое худшее — это его императивный API, в котором мы вынуждены управлять его показом через suspend функции show/hide, а также приходится оборачивать контент экрана в ModalBottomSheetLayout. Это очень не удобно, когда нужно показать не статический контент, а полноценный экран с динамическим отображением данных и своей логикой.

😀Решить проблему можно с помощью кастомной декларативной обертки

Как это работает
Показываем Bottom Sheet, если ассоциированный с ним стейт ≠ null, иначе скрываем

Особенности реализации
Нужно уметь показывать предыдущий контент, пока bottom sheet скрывается, несмотря на то, что данных уже нет
Нужно правильно вызывать лямбду onDismiss и здесь можно допустить ошибку:
😀 Завязываться на confirmValueChange не вариант, так как теперь этот callback вызывается множество раз
😀 Отслеживание изменения sheetState.targetValue также может привести к проблемам, так как targetValue будет Hidden даже если вы не до конца скрыли Bottom Sheet

Проблема😀
На текущий момент если скрыть Bottom Sheet через изменение стейта, то его скрытие можно перехватить жестом и тогда останемся в неконсистентном состоянии. Решить проблему можно либо скрытием Bottom Sheet без анимации, либо не занулять стейт, пока он не будет полностью скрыт.

😀 Гораздо лучше дела обстоят в Material 3, там из коробки Bottom Sheet уже декларативный и в нем было решено большинство проблем, только вот далеко не все используют M3 в своих проектах и, чтобы использовать его реализацию, придется копировать к себе кучу исходного кода, что тоже не круто. Таким образом если вы еще не перешли на компоузовский Bottom Sheet, то лучше пока и не торопиться😉

А как вы боретесь с проблемами с Bottom Sheet в своем проекте?

#Compose
Please open Telegram to view this post
VIEW IN TELEGRAM
На ближайшей конференции Mobius я буду участвовать сразу в двух сессиях:

🗓 2 ноября мы вместе с Дмитрием Григорьевым проведем крутую игру-квиз по Jetpack Compose в онлайн формате, где осветим большое количество самых разных и интересных тем из мира Compose, а участники смогут посоревноваться друг с другом за классные призы.

🗓 10 ноября уже оффлайн в Санкт-Петербурге буду рассказывать про подкапотную магию работы стейта в Compose, рассмотрим почему он далеко не так прост, как кажется на первый взгляд и почему он является важнейшим механизмом в работе всего Compose.

🎁 В связи с этим хочу устроить аттракцион невиданной щедрости провести розыгрыш билета на ближайшую конференцию Mobius. Из обязательных условий — быть подписанным на telegram канал @kotlin_adept, а также буду очень благодарен, если поделитесь ссылкой на данный канал со своими коллегами. Ведь, чем больше аудитория, тем больше мотивации чаще постить качественный контент🙂

❗️Для участия в розыгрыше достаточно нажать на кнопку «Принять участие» под следующим постом и мы определим победителя через 24 часа.

P.S. Пожалуйста, принимайте участие в розыгрыше только, если вы планируете смотреть конференцию, пусть билет достанется тому, кому он действительно нужен!
Please open Telegram to view this post
VIEW IN TELEGRAM
Розыгрыш билета на конференцию Mobius 2023 Autumn
Kotlin Adept Notes
Розыгрыш билета на конференцию Mobius 2023 Autumn
Результаты розыгрыша:

Победитель:
1. Danil

Проверить результаты
Code coverage для UI тестов

⚙️ Тема анализа покрытия кода Unit тестами уже не нова, для Java уже давным-давно существует библиотека JaCoCo, для Kotlin есть официальный Gradle Plugin Kover. Оба этих инструмента позволяют анализировать ваш код и генерировать различные отчеты о его покрытии Unit тестами. Это безусловно полезные инструменты для улучшения качества вашего кода и уверенности в нем.

⚙️ Но как обстоят дела с UI тестами? Можем ли мы с помощью них проанализировать процент покрытия тестами нашей бизнес логики?

⚙️На этот вопрос ответил мой коллега из Контура, Игорь Гордеев, в своей статье на Хабре. Он рассказал, как с помощью библиотеки VisualFSM, конечных автоматов и щепотки кодогенерации сделать такое покрытие и упростить жизнь и тестировщикам, и разработчикам в тысячу раз!

Приятного чтения 🤯

#Android #Testing
Please open Telegram to view this post
VIEW IN TELEGRAM
Compose Quiz

🔴Ребят, напоминаю, что сегодня пройдет квиз по Jetpack Compose в 12:15 (мск) на сайте Mobius. И принять участие вы можете абсолютно бесплатно так как это Community Day, достаточно просто зарегистрироваться по ссылке.

Мы подготовили аж 40 😱 вопросов по Compose и осветили все самые значимые темы, знание которых определенно поможет вам в повседневной разработке на Compose.

Так что приходите посоревноваться друг с другом, а тот кто наберёт больше всего баллов, получит хороший приз🏅
Please open Telegram to view this post
VIEW IN TELEGRAM
VPN, который не заблокируют

🌐Ни для кого не секрет, что сейчас все больше протоколов VPN подвергается блокировке — это и WireGuard, и OpenVPN, и другие, но решение есть!
Причём собрать свое мобильное приложение с VPN, которое не боится блокировок достаточно легко. Для этого нам потребуется связка WireGuard + xRay.

Принцип работы

😀xRay — это прокси-сервер, который умеет маскировать трафик под браузерный (TLS). Нам лишь нужно запустить VPN туннель и весь трафик пропускать через него.

Пример на Android

😀Запускаем xRay с переданной конфигурацией. В inbounds указываем входящий трафик от WireGuard с localhost и протоколом dokodemo-door, а в outbounds исходящий трафик на ваш сервер, тут можно использовать либо облегченный протокол vless, либо полноценный протокол с шифрованием vmess.

LibXray.runXray("", configFile.absolutePath, 0)


😀Подготавливаем VpnService, чтобы ваше приложение могло создавать VPN туннель.

GoBackend.VpnService.prepare(context)


😀Поднимаем VPN туннель. Внутри конфига в Endpoint указываем localhost, чтобы весь трафик шел в xRay.

val backend = GoBackend(applicationContext)
val tunnel: Tunnel = WireGuardTunnel() // Ваш класс, реализующий интерфейс Tunnel
val config: Config = ... // Ваш конфиг для WireGuard
backend.setState(tunnel, Tunnel.State.UP, config) // Обязательно вызывать с фонового потока

Как видим реализовать свое приложение для VPN с данными либами довольно не сложно 🤔. И так как эти либы написаны на Go, мы их можем запустить где угодно, будь то Android, iOS или любая другая платформа.

Если у вас остались вопросы и тема заинтересовала, то пишите комменты, попробую помочь, чем смогу👇
Please open Telegram to view this post
VIEW IN TELEGRAM
⚪️Ребят, напомню, что сегодня на Мобиус в 16:00 мск будет мой доклад про подкапотную магию стейта в Compose. Там будет много всего интересного, а именно:

🟢Поговорим про устройство снапшотов и узнаем причем здесь базы данных
🔵Разберемся как сделать свой стейт
🟣Рассмотрим как происходит чтение и запись, как снапшоты изолируются друг от друга
🔵А также ответим на вопрос, как при изменении стейта происходит рекомпозиция функций

Если вы находитесь на площадке Мобиуса, то всех буду ждать в первом зале или приходите посмотреть доклад онлайн, если есть такая возможность.

До встречи❤️
Please open Telegram to view this post
VIEW IN TELEGRAM
Compose Snapshots

После доклада меня много спрашивали, а зачем мне вообще знать про снапшоты? Где эти знания применить на практике?

💡 И на мой взгляд, в первую очередь их нужно знать, чтобы разобраться, а каким образом изменение State внутри Composable функции приводит к рекомпозиции?

✳️Давайте сначала вспомним, что вообще такое Snapshot

Это механизм, используемый внутри Compose State и не только, для работы с множественными изолированными копиями состояния. Снапшоты также позволяют сделать безопасное изменение стейта конкурентно без блокировок. Можете думать про снапшоты как про транзакции в базах данных или как про ветки в git.

✳️Причем тут снапшоты и рекомпозиция?

Все Composable дерево заворачивается в Snapshot, поэтому на самом деле изменение стейта не происходит глобально, а происходит внутри снапшота, поэтому снапшот может отследить каждое чтение и запись любого стейта внутри, так Compose понимает какие части экрана нужно перерисовать в дальнейшем.


private inline fun <T> composing(
composition: ControlledComposition,
modifiedValues: IdentityArraySet<Any>?,
block: () -> T
): T {
val snapshot = Snapshot.takeMutableSnapshot(
readObserverOf(composition), writeObserverOf(composition, modifiedValues)
)
try {
return snapshot.enter(block)
} finally {
applyAndCheck(snapshot)
}
}


✳️Где снапшоты могут пригодиться на практике?

🟠Если вдруг вы захотите написать свою либу поверх compose runtime
🟡Для безопасного изменения стейта с нескольких потоков
🔵Для создания своего изменяемого Stable типа, отслеживаемого Compose
🔵Для всего, на что способно ваше воображение! Например, почему бы не работать с БД через Compose State? Что? Да! Это тоже возможно благодаря снапшотам.

Но самое крутое и полезное применение снапшотов я покажу в следующем посте, так что stay tuned 💻

#Compose #Snapshots
Please open Telegram to view this post
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
Compose withAnimation

В SwiftUI есть очень классная фича withAnimation, позволяющая сделать анимацию вьюшки просто путем изменения состояния, а сама анимация произойдет как по волшебству.


@State private var showDetail = false

var body: some View {
VStack {
Button("Show details") {
withAnimation {
showDetail.toggle()
}
}

if showDetail {
Text("Details")
}
}
}


Не справедливо, что такого механизма нет из коробки в Compose, и инженер из Google решил исправить это недоразумение. Он сделал свой аналог withAnimation и реализовал это с помощью Snapshot API, про который мы говорили ранее.

Как это работает?

1. Создается пустой словарь состояний для анимации
2. Выполняется лямбда блок внутри Snapshot, в этой лямбде могут происходить изменения стейта
3. У Snapshot вызывается writeObserver при каждой записи в State и заполняется информация для анимации
4. Данные мапятся в другой тип, откуда достаются измененные значения
5. Уничтожается Snapshot, чтобы не допустить утечек памяти, при этом изменения не применяются глобально! «Все что произошло в снапшоте, остается в снапшоте»©
6. Анимируются значения


internal suspend fun withAnimation(
adapterRegistry: StateObjectAdapterRegistry,
animationSpec: AnimationSpec<Any?>,
block: () -> Unit
) {
val statesToAnimate = mutableMapOf<Any, StateObjectAdapter>() // 1
val snapshot = Snapshot.takeMutableSnapshot(
writeObserver = { changedState ->
statesToAnimate[changedState] = checkNotNull(adapterRegistry.getAdapterFor(changedState)) // 3
}
)
val targetValues = snapshot.enter {
block() // 2
buildTargetValues(statesToAnimate) // 4
}
snapshot.dispose() // 5

animateValues(targetValues, animationSpec) // 6
}


Если у вас есть еще идеи как можно применить снапшоты, делитесь своими мыслями в комментариях👇

#Compose #Snapshots #Animations
Сегодня на конференции YaTalks будет очень крутая сессия, на которой я (Алексей Панов) и Алексей Гладков будем стараться максимально закопать Flutter и возвысить KMP, другая же команда, состоящая из Геннадия Евстратова и Евгения Сатурова, будут пытаться нам противостоять.

Все это будет проходить в формате настоящих дебатов!

Так что регистрируйтесь и приходите посмотреть прямой эфир в 19:00 (мск). Заруба должна получиться просто огненной 🔥

А если вы хотите внести свой вклад в победе над Flutter, то пишите какие бы вы задали самые неудобные вопросы команде Flutter в комментариях и возможно мы возьмем их в эфир.
Please open Telegram to view this post
VIEW IN TELEGRAM
По мотивам прошедших дебатов хотелось бы обсудить плюсы и минусы Flutter в сравнении с KMP и Compose Multiplatform для разработки мобильных приложений.

👍Плюсы Flutter

▫️Высокопроизводительный графический движок Impeller
▫️Очень простой порог входа
▫️Куча готовых плагинов
▫️На сегодняшний день гораздо популярнее KMP
▫️В релизе с 2018 года, в отличие от вышедшего в этом году в релиз KMP и тем более Compose MP for iOS, который все ещё в альфе
▫️Flutter на iOS работает быстрее и стабильнее, чем Compose для iOS на сегодняшний день
▫️Есть киллер фичи Hot Reload / Hot Restart ускоряющие разработку
▫️На Flutter можно официально разрабатывать под Аврору и Фуксию (зачем — это уже другой вопрос)

👎Минусы Flutter

🔸Очень больно переводить существующий проект на Flutter, с KMP это можно делать просто и постепенно
🔸Dart по сравнению с Kotlin выглядит очень устаревшим, приходится писать гораздо больше кода
🔸Многопоточность в Dart с изолятами довольно ограничена по сравнению с возможностями и гибкостью корутин в Kotlin
🔸Верстка одного и того же экрана на Flutter получается примерно в 2 раза больше чем на Compose
🔸Более примитивная система сборки по сравнению с Gradle
🔸Кодогенерацию нельзя органично встроить в процесс сборки, про компиляторные плагины даже речи не идёт
🔸Нетипобезопасные платформенные каналы, все креши будут в рантайме, если где-то ошибётесь (но есть альтернатива в виде Pigeon)
🔸Множество публичных плагинов спорного качества
🔸Нет официального решения для организации многомодульного проекта (есть только инструмент Melos)
🔸Dart используется только во Flutter, в отличие от Kotlin, который используется в различных областях

Это все моменты, что я смог вспомнить, если у вас есть что добавить, то не стесняйтесь писать свои мысли в комментариях ⌨️

#Flutter #KMP
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Compose Broadcast (Alex Panov)
This media is not supported in your browser
VIEW IN TELEGRAM
Автор крутого доклада про компиляторные плагины для Compose с предыдущего Mobius опубликовал исходники плагинов на GitHub.

Там очень много всего интересного и полезного:
👉 Анализ стабильности параметров Composable функции
👉 Подсветка рекомпозиций в UI
👉 Автоматическая генерация и удаление testTag
👉 Логирование причин рекомпозиции и другое

Эти плагины наконец-то решают извечную проблему анализа лишних рекомпозиций и оптимизаций вашего кода в Compose, теперь делать высокопроизводительные приложения стало гораздо проще!

#compose #plugins
2025/07/07 10:28:41
Back to Top
HTML Embed Code: