Forwarded from Android Broadcast (Кирилл Розов)
YouTube
Live Edit for Jetpack Compose
Live Edit introduces a new way to edit your app’s Compose UI by instantly deploying code changes to the running application on a physical device or emulator.
Live Edit for Jetpack Compose → https://goo.gle/4570EAM
Jetpack Compose tutorial → https://goo.gle/3OyeULw…
Live Edit for Jetpack Compose → https://goo.gle/4570EAM
Jetpack Compose tutorial → https://goo.gle/3OyeULw…
👍12👎3
#android #edgetoedge
Please open Telegram to view this post
VIEW IN TELEGRAM
YouTube
Edge-to-edge and insets | Jetpack Compose Tips
Learn how to easily make your Android apps go edge-to-edge using Compose in this Compose Tips video - ensuring your UI has a modern, delightful UI.
This video will show you the three simple steps to safely handle insets to prevent content from overlapping…
This video will show you the three simple steps to safely handle insets to prevent content from overlapping…
👍15
Важное изменение в Compose 1.10: pausable composition в lazy prefetch теперь включен по умолчанию. Это фундаментальное улучшение в работе runtime, которое значительно уменьшает лаги при сложных UI-нагрузках.
Раньше композиция, раз начавшись, должна была выполниться до конца. Если она была сложной (много элементов, тяжелые вычисления), это могло заблокировать главный поток дольше, чем длится один кадр и получали Freeze Frame и визуальные лаги скролла.
Теперь Compose Runtime может приостанавливать работу, если время на отрисовку кадра заканчивается, и продолжить её в следующем интервале. Особенно эффективно это работает в связке с предзагрузкой (prefetch) ленивых списков.
🔄 Как это работает с Lazy layouts:
// Увеличиваем окно кэша для большего пространства предзагрузки
val cacheWindow = LazyLayoutCacheWindow(
ahead = 0.5f, // 50% вперед
behind = 0.3f // 30% назад
)
val state = rememberLazyListState(cacheWindow = cacheWindow)
LazyColumn(state = state) {
items(heavyItems) { item ->
HeavyComposable(item) // Теперь не заблокирует UI
}
}
🎯 Ключевые преимущества:
1. Плавная прокрутка — даже с тяжелыми элементами
2. Композиция подстраивается под время для отрисовки кадра — композиция «уступает» место другим операциям
3. Никакой сложной настройки — не требует изменения кода приложения
Эта оптимизация — часть продолжающейся работы Google над производительностью Compose. Уже пробовали? Делитесь наблюдениями в комментариях!
#Compose #Производительность #AndroidDev #JetpackCompose
Please open Telegram to view this post
VIEW IN TELEGRAM
👍46🔥17❤7
Мэтт МакКенна выпустил библиотеку, которая превращает рекомпозиции в обычные test assertions. Dejavu решает проблемы постоянного мониторинга за рекомпозияцими без изменений в продакшн-коде — только
Modifier.testTag(), который скорее всего у вас уже есть:// Пример теста
@get:Rule
val composeTestRule = createRecompositionTrackingRule()
@Test
fun incrementCounter_onlyValueRecomposes() {
composeTestRule.onNodeWithTag("inc_button").performClick()
composeTestRule.onNodeWithTag("counter_value")
.assertRecompositions(exactly = 1)
composeTestRule.onNodeWithTag("counter_title")
.assertStable() // ноль рекомпозиций
}
Когда тест падает, получаете структурированный отчёт:
UnexpectedRecompositionsError: testTag='product_header'
Composable: demo.app.ui.ProductHeader (ProductList.kt:29)
Expected: exactly 0 recomposition(s)
Actual: 1 recomposition(s)
All tracked composables:
ProductListScreen = 1
ProductHeader = 1 <-- FAILED
ProductItem = 1
Recomposition timeline:
#1 at +0ms — param slots changed: [1] | parent: ProductListScreen
Possible cause:
1 state change(s) of type Int
Parameter/parent change detected (dirty bits set)
Видно какой composable, сколько раз рекомпозировался и почему. Под капотом используется
CompositionTracer API из compose-runtime 1.2.0, никаких Gradle-плагинов и байткод-манипуляций. Запускается как instrumented тест.#Android #AndroidDev #Compose #JetpackCompose
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥48👏7❤5👎4🤔2👍1
This media is not supported in your browser
VIEW IN TELEGRAM
Наткнулся на отличный разбор от ребят из Т-Банка — они переписывали главный экран с XML+View на Compose и столкнулись с проблемой, которую многие, думаю, обходят стороной.
Суть: есть
LazyColumn, в нём элемент с animateContentSize(). Когда элемент расширяется по высоте, нижние карточки не успевают сместиться в такт — небольшой, но заметный рассинхрон.Казалось бы — поменяй
spring на tween, синхронизируй тайминги и готово. Спойлер: нет.1️⃣ Замена на линейный
tween — разницы визуально почти ноль. Скорость placementSpec тоже не влияет на смещение нижних айтемов при ресайзе верхнего.2️⃣ Установка
placementSpec = null — синхронизация появляется, но полностью ломает анимацию перемещения айтемов. Не вариант.3️⃣ Попытка написать свой модификатор — обречена с самого начала.
LazyLayoutAnimationSpecsNode помечен как internal, а внутренний LazyLayoutItemAnimator, который реально управляет анимациями, недоступен снаружи. Скопировать код не выйдет — каст по типу вернёт null, и вся механика рассыпается. Форкать 5000+ строк LazyColumn — очевидно нет.Самое интересное — анимация удаления при кастомной реализации не работает в принципе: к моменту
DisposableEffect.onDispose элемент уже удалён из дерева. А стандартный animateItem работает на уровне layout-фазы и может буквально «удерживать» элемент в дереве до окончания анимации.Итог у команды — оставили
RecyclerView для списка, айтемы внутри на Compose. Костыль, но рабочий, пока Google не откроет нужные API. Issue уже создан, подписывайтесь если сталкивались.Я лично не сталкивался с таким кейсом в продакшне, но статья хорошо показывает, где у Compose сейчас реальные границы расширяемости и что без View пока никуда.
#Android #Compose #AndroidDev #JetpackCompose #Анимация
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥37👎13❤8👍2
В Compose 1.11.0 завезли новый интерфейс PreviewWrapper и аннотацию @PreviewWrapperProvider. Идея простая: вместо того чтобы оборачивать каждый Preview в свою тему, CompositionLocalProvider, моки навигации и прочее, ты один раз описываешь обёртку и просто навешиваешь её аннотацией.
Выглядит это так:
class ThemeWrapper : PreviewWrapper {
@Composable
override fun Wrap(content: @Composable () -> Unit) {
JetsnackTheme {
content()
}
}
}@PreviewWrapperProvider(ThemeWrapper::class)
@Preview
@Composable
private fun ButtonPreview() {
Button(onClick = {}) {
Text("Demo")
}
}
Сценарии где может пригодиться:
👉 встроить тему в превью
👉 подложить FakeNavController или LocalActivity
👉 обернуть в Hilt/Koin провайдер с тестовыми зависимостями
👉 прокинуть моки через CompositionLocal
Раньше для этого либо городили собственную функцию и вызывали её руками в каждом превью, либо писали кастомные
@Preview аннотации с ограниченным набором параметров.‼️ ВАЖНО: фича работает только в Android Studio Panda 4 и выше. В более старых версиях аннотация просто проигнорируется.
#compose #jetpackcompose #android
Please open Telegram to view this post
VIEW IN TELEGRAM
👍55👎5
This media is not supported in your browser
VIEW IN TELEGRAM
v1 умел одно — применять изменения значений без rebuild: цвет, padding, текст, размеры — результат за секунду, приложение живёт. Добавить composable, переставить layout, поменять control flow — всё это шло в полный rebuild.
v2 убирает эту границу. Без рестарта приложения: добавляем и удаляем composable-функции, меняем структуру layout, переписываем conditional UI. И главное — заменяем целый экран на совершенно другой код. Навигационный стек и
remember-состояние сохраняются.Технически v2 запускает изменения через собственный интерпретатор, обходя ограничения ART на переопределение классов — тот же подход, что у Flutter. При слишком глубоких изменениях деградирует плавно: сбрасывает composition с сохранением
rememberSaveable и ViewModel, в крайнем случае рестартит Activity. Полный rebuild нужен только при изменении сигнатур функций, иерархии классов и inline-функций.В v2 также MCP-сервер: AI-агент триггерит reload, снимает скриншот с устройства, итерирует по вариантам. Claude Code и Cursor работают из коробки через file watcher.
Поставить: IDE-плагин вручную с бета-канала Marketplace + Gradle-плагин
2.0.0-beta01:[plugins]
hotswan-compiler = { id = "com.github.skydoves.compose.hotswan.compiler", version = "2.0.0-beta01" }
Инструмент платный инструмент, лицензия и цены на hotswan.dev/pricing.
Такого охвата hot reload в нативном Compose ещё не было. Аргумент Flutter про быстрый feedback loop заметно слабеет.
#Compose #JetpackCompose #AndroidDev
Please open Telegram to view this post
VIEW IN TELEGRAM
👎23👍14