Forwarded from StartAndroid
Сначала Роман Елизаров выступил на Java Rock Star Meetup с докладом про опыт разработчика (DX,Developer Experience). По сути про то, как современный разработчик тратит слишком много сил не на бизнес-задачу, а на обслуживание сложности вокруг себя: выборы, переключения, бойлерплейт, инфраструктурную возню, разрозненные инструменты.
И не так давно нам попался отчет Chainguard Engineering Reality Report 2026. Это уже не взгляд одного сильного практика, а международный срез: 1200 инженеров и технологических руководителей из США, Великобритании, Германии и Франции рассказали, что именно сегодня ломает Developer Experience. Картина оказалась удивительно знакомой: рутина съедает время, поддержка и сопровождение побеждает инновации, инструменты не стыкуются между собой, AI помогает, но не магически, а ровно настолько, насколько хорошо встроен в процесс.
Нам показалось интересным не просто пересказать оба материала, а собрать в единую картину. Где выводы российского специалиста и международной команд рифмуются между собой. Где Елизаров идет глубже и объясняет причины. И где начинается самое интересное - то есть различия между глобальной картиной и тем, как эта тема ощущается на российской почве.
#development
https://habr.com/ru/companies/axiomjdk/articles/1023926/
Хабр
Опыт разработчика как экономика внимания
Привет, Хабр! Эта статья выросла из двух материалов, которые неожиданно хорошо коррелируют между собой. Источники: доклад Романа Елизарова ( @elizarov ) Отчет Chainguard Engineering Reality Report...
Forwarded from Kotlin Multiplatform Broadcast
🔥Главное произошло в KMP части - Flows теперь экспортируются в Swift как AsyncSequence по умолчанию. Это значит, что вся эта суета с обёртками и callback-based API просто исчезает. Пишешь Flow на Kotlin-стороне, Swift видит AsyncSequence, готово.
🔥 Swift packages теперь импортируются прямо в Gradle без всех этих танцев с CocoaPods. Firebase, другие iOS либы импортируются через build.gradle.kts как обычные зависимости.
🔥 Collection literals в экспериментальной стадии. Теперь не только в аннотациях. Сомнительно... ну ОКЭЙ!
🔗 Больше подробностей в документации и @kotlin_broadcast
#kotlin #kmp #swift #release
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Kotlin Multiplatform Broadcast
// Вместо
val shapes: MutableList<String> =
mutableListOf("triangle", "square", "circle")
// Можно будет использовать новый синтаксис
val shapes: MutableList<String> =
["triangle", "square", "circle"]
class DoubleMatrix(vararg val rows: Row) {
companion object {
operator fun of(vararg rows: Row) = DoubleMatrix(*rows)
}
class Row(vararg val elements: Double) {
companion object {
operator fun of(vararg elements: Double) = Row(*elements)
}
}
}
val identityMatrix: DoubleMatrix = [
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
]Для тех кто создает много коллекций с наполнением станет проще. Но вот как часто кроме тестов приходится этим заниматься? Жил без литералов долго и ещё проживу
#Kotlin
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Kotlin Multiplatform Broadcast
Теперь работать с Kotlin Flow в KMP станет проще так как будет автоматическое конвертирование в удобный для Swift формат
fun flowOfStrings(): Flow<String> = flowOf("hello", "any", "world")var actual: [String] = []
// Type String is correctly inferred from Kotlin
for try await element in flowOfStrings().asAsyncSequence() {
actual.append(element)
}
🔗 Как настроить Swift to Kotlin Export
#Kotlin #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Compose Broadcast
В 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
Forwarded from Android Good Reads
Strong Skipping Mode не делает типы stable, лишь позволяет skip’ать
Внутри много кода, а не воды для самопроверки
composable с unstable параметрами, если передан тот же самый объект по ссылке ===, тогда как stable типы по-прежнему сравниваются через equals() из-за этого он не помогает, если создаются новые инстансы (типа listOf(), copy()), и рекомпозиция всё равно происходит, поэтому stability позволяет пропускать перерисовку для разных объектов с одинаковыми данными, а Strong Skipping - это оптимизация для случаев с переиспользованием одной и той же ссылки, а не замена пониманию стабильностиВнутри много кода, а не воды для самопроверки
Forwarded from Mobile Compose
This media is not supported in your browser
VIEW IN TELEGRAM
#Compose #UI #Animation #Debugging
🎨 Compose 1.11.0 — Визуальная отладка анимаций
Shared Elements анимации и работа с Modifier.animatedBounds стали прозрачнее благодаря инструменту LookaheadAnimationVisualDebugging.
Ключевые возможности:
▫️ Прямо в Runtime можно увидеть траектории движения объектов и их целевые границы (target bounds).
▫️ Визуализация помогает понять, почему элемент «прыгает» или обрезается при переходе между экранами.
Инструмент полезен в первую очередь для сложных UI-переходов, где раньше приходилось полагаться только на логи и интуицию.
🎨 Mobile Compose 💬 Чат канала
♾
Shared Elements анимации и работа с Modifier.animatedBounds стали прозрачнее благодаря инструменту LookaheadAnimationVisualDebugging.
Ключевые возможности:
Инструмент полезен в первую очередь для сложных UI-переходов, где раньше приходилось полагаться только на логи и интуицию.
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Compose Broadcast
This media is not supported in your browser
VIEW IN TELEGRAM
#Compose
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Compose Broadcast
// Пример использования
LookaheadAnimationVisualDebugging(
overlayColor = Color(0x4AE91E63),
isEnabled = true,
multipleMatchesColor = Color.Green,
isShowKeylabelEnabled = false,
unmatchedElementColor = Color.Red,
) {
SharedTransitionLayout {
CompositionLocalProvider(
LocalSharedTransitionScope provides this,
) {
// your content
}
}
}
#Compose
Forwarded from Compose Broadcast
До недавнего времени стилизация компонентов в Compose делалась через параметры Composable функции (
color = ..., padding = ...) или модификаторы. Это работало, но могло приводить к:👉 избыточным рекомпозициям при смене состояний;
👉 дублированию кода для разных вариаций кнопок/карточек;
👉 сложностям с анимацией переходов между стилями.
Google представил Styles API (экспериментальный, о причинах такого статуса ниже) — новый подход, вдохновлённый CSS. Нужен Jetpack Compose 1.11.0 или свежее
Ключевые возможности
Стили вычисляются на этапах layout и drawing, минуя composition. Это значит, что изменение стиля (например, при наведении) не вызывает рекомпозицию родительских компонентов.
Без кучи
if/else и animate*AsState. Просто описываете стиль для каждого состояния (нажат, сфокусирован, включён, ошибка и т.д.).Переходы между состояниями анимируются автоматически с физическими параметрами (демпфирование, жёсткость). Не нужно писать
Animatable или Transition.// Пример использования стиля
// Определяем стиль для кнопки
val buttonStyle = Style {
default {
backgroundColor = Color.Blue
shape = RoundedCornerShape(8.dp)
}
pressed {
backgroundColor = Color.DarkBlue
scale = 0.98f
}
disabled {
backgroundColor = Color.Gray
alpha = 0.5f
}
}
// Используем
Button(
onClick = { /* ... */ },
style = buttonStyle, // ← вместо кучи параметров
enabled = isEnabled
) {
Text("Нажми меня")
}
style — новый параметр, который появляется у всех компонентов Material 3 и базовых (Box, Row, Column).⚠️ Styles API не заменяет модификаторы
Модификаторы остаются для позиционирования, кликов, размеров. Styles API заменяет только внутреннюю стилизацию (цвета, формы, тени, отступы внутри компонента). Это как разделение «темы» и «вёрстки».
API остаётся экспериментальным пока на него полностью не перейдет Material 3 Compose, чтобы учесть все необходиcмоти API и не делать breaking changes. По работе оно уже стабильно, но вот изменения в коде, возможно, будут.
🔗 Официальная документация
В целом с таким подходом уже привыкли работать в Android XML Layout, то почему бы сразу было его не использовать? Тем более в Compose плюсы очевидны!
#Compose #Material
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Kotlin Multiplatform Broadcast
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Android Good Reads
This media is not supported in your browser
VIEW IN TELEGRAM
Compose анимации без пересборки
Дебажить анимации трудно и долго. Hot reload помогает и тут. Внутри анимация
Дебажить анимации трудно и долго. Hot reload помогает и тут. Внутри анимация
Grid, кастомная анимация волны на Canvas, изменение типа анимации tween -> spring, изменение animateContentSizeHotSwan обновляет примитивы в обход компиляции Gradle, за счет этого и получается такой результатForwarded from Android Broadcast
🤯 Те кто использовал Jetpack Paging 3 знают насколько он может справиться только с базовыми сценариямми. Шаг влево или вправо - уже боль!
Сегодня хочу познакомить вас с альтернативой — библиотекой🐱 Paginator. Это KMP-решение для пагинации, которое решает те самые проблемы, где Paging 3 начинает "буксовать".
Paginator построен на отличной модели от Jetpack Paging 3: страница — это адресуемая ячейка в кэше, кэш — обычная структура данных, а навигация — обычные методы.
1️⃣ ✅ Адресуемые страницы
Прямые методы
2️⃣ ✅ Мутации по запросу
3️⃣ ✅ Сохраняемое состояние
Кэш — обычная структура данных, которую можно сериализовать через
4️⃣ ✅ Библиотека, а не фреймворк
Написан на чистом Kotlin без платформенных зависимостей и живёт в
5️⃣ ✅ Курсорная пагинация "из коробки"
Отдельный класс
🔗 Оригинальная статья на Хабре
🐱 Репозиторий Paginator
#Android #AndroidDev #KMP
Сегодня хочу познакомить вас с альтернативой — библиотекой
Paginator построен на отличной модели от Jetpack Paging 3: страница — это адресуемая ячейка в кэше, кэш — обычная структура данных, а навигация — обычные методы.
Прямые методы
goNextPage(), goPreviousPage() и jump(Bookmark). Deeplink на сообщение из пуша решается одной строкой.MutablePaginator предоставляет CRUD-операции: replace { it.id == 42 }, removeWhere { it.deleted }. Один лайк меняет один элемент без инвалидации всей страницы.Кэш — обычная структура данных, которую можно сериализовать через
kotlinx.serialization. Методы serializeToJson() и restoreFromJson() решают проблему Process Death без плясок с бубном.Написан на чистом Kotlin без платформенных зависимостей и живёт в
commonMain. Логика пагинации становится частью общего доменного слоя KMP, не копируется между платформами и не имеет двух реализаций.Отдельный класс
CursorPaginator с единой моделью состояний, транзакций и сериализации.Paginator — это не обёртка над Paging 3. Это другая модель, переосмысливающая ключевые типы. Обёртка просто не смогла бы дать ни мутации элемента, ни сериализации кэша.
🔗 Оригинальная статья на Хабре
#Android #AndroidDev #KMP
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Android Broadcast
NoSuchMethodError или ClassNotFoundException, знакома многим. Причина — тихий конфликт версий. Gradle по умолчанию старается брать самую новую версию из всех найденных, но срабатывает не всегда. В разных модулях одного проекта могут спокойно жить разные версии одной библиотеки (например, okhttp 4.9.0 в модуле А и 4.11.0 в модуле Б). Gradle не считает это конфликтом, потому что модули изолированы. В рантайме при передаче объекта между модулями — ClassCastException. Особенно больно это бьет в KMP, где общая бизнес-логика связывает всё в единую цепочку.Плагин
# Пример работы плагина
Version conflict detected: org.slf4j:slf4j-api
- version 2.0.17 via:
- project :app -> ch.qos.logback:logback-classic:1.4.11 -> org.slf4j:slf4j-api:2.0.17
...
- version 1.7.25 via:
- project :app -> org.apache.logging.log4j:log4j-slf4j-impl:2.17.1 -> org.slf4j:slf4j-api:1.7.25
Такая проактивная проверка помогает фиксить конфликты еще на этапе разработки и писать более стабильный код. Попробуйте.
#Gradle
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Android Broadcast
Данные официального бенчмарка Google
#AI #Benchmark
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Android Good Reads
Android CLI зацикливает на себе весь цикл разработки для андроид c агентами. Вам не нужно прыгать и искать какие скилы подтянуть в проект, какой контекст по андроид разработке предоставить. All-in-one от гугла (sdk, эмуляторы, запуск билдов ... )
Ни для чего другого, кроме как мобильной разработке, это не подходит, так что поставить точно рекомендую!
Ни для чего другого, кроме как мобильной разработке, это не подходит, так что поставить точно рекомендую!
Forwarded from Антоха Разраб про AI
This media is not supported in your browser
VIEW IN TELEGRAM
Все идет по пути к тому что у каждого тула будет CLI версия. Обсидиан запустил пару месяцев назад, а вот и Google (Пере)представляет Android CLI
По ощущениям оч кастрированный и является надстройкой над
(c) Антоха Разраб про AI
По ощущениям оч кастрированный и является надстройкой над
adb(c) Антоха Разраб про AI
Forwarded from Android Good Reads
Если вы используете Metro, в качестве DI, то знайте что оно стало стабильным. Свеженаписанный легковесный DI для android
Релиз ноутс: https://github.com/ZacSweers/metro/releases/tag/1.0.0
Релиз ноутс: https://github.com/ZacSweers/metro/releases/tag/1.0.0
Forwarded from Android Good Reads
Что нового в Compose в Апреле
👉 v2 API тестирования стал дефолтным,
👉 Shared Elements: новый composable
👉 Поддержка трекпадов: теперь трекпады распознаются как
👉 Compose runtime: новый
👉 Аннотация
👉 Готовимся что в 1.12.0
Android Good Reads
compose-bom:2026.04.01 или Compose 1.11.0👉 v2 API тестирования стал дефолтным,
StandardTestDispatcher вместо UnconfinedTestDispatcher. Коррутины в тестах больше не выполняются сразу — очередь, пока виртуальные часы не продвинутся👉 Shared Elements: новый composable
LookaheadAnimationVisualDebugging показывает границы анимаций, траектории и несовпадения👉 Поддержка трекпадов: теперь трекпады распознаются как
PointerType.Mouse, а не Touch. Исправляет скролл вместо выделения, добавляет поддержку жестов и десктопных контекстных меню👉 Compose runtime: новый
HostDefaultProvider позволяет библиотекам (особенно KMP) получать сервисы без зависимости от compose-ui👉 Аннотация
@PreviewWrapper для переиспользуемых обёрток превью:class ThemeWrapper: PreviewWrapper {
@Composable
override fun Wrap(content: @Composable (() -> Unit)) {
JetsnackTheme {
content()
}
}
}
@PreviewWrapperProvider(ThemeWrapper::class)
@Preview
@Composable
private fun ButtonPreview() {
// JetsnackTheme in effect
}👉 Готовимся что в 1.12.0
compileSdk обновится до 37. Будет много боли потому что 37 Sdk тянет за собой AGP 9Android Good Reads
Forwarded from Android Good Reads
Что нового в Compose в Апреле Часть 2
Экспериментальные API:
👉 Styles API: декларативные стили компонентов с автоматическими анимациями. Судя по бенчмаркам, быстрее модификаторов
👉 MediaQuery: адаптация под окружение (размер окна, тип клавиатуры, режим стола и т.д.)
👉 Grid: 2D-раскладка с треками, фракциями (fr) и авто-размерами
👉 FlexBox: мощный контейнер с гибким распределением пространства, wrap'ом и выравниванием. Замена Row/Column/Flow
👉 Новый
Android Good Reads
Экспериментальные API:
👉 Styles API: декларативные стили компонентов с автоматическими анимациями. Судя по бенчмаркам, быстрее модификаторов
👉 MediaQuery: адаптация под окружение (размер окна, тип клавиатуры, режим стола и т.д.)
👉 Grid: 2D-раскладка с треками, фракциями (fr) и авто-размерами
👉 FlexBox: мощный контейнер с гибким распределением пространства, wrap'ом и выравниванием. Замена Row/Column/Flow
👉 Новый
SlotTable: ускорение случайных правок в дереве композиции (пока опционально через флаг)Android Good Reads
Forwarded from Compose Broadcast
В апрельском релизе Jetpack Compose под номером 1.11 в compose-runtime завезли четыре новых API:
HostDefaultProvider, LocalHostDefaultProvider, HostDefaultKey и ViewTreeHostDefaultKey. Плюс билдер compositionLocalWithHostDefaultOf для авторов библиотек.Он позволяют объявить
CompositionLocal, у которого дефолтное значение резолвится из окружения хоста — например, через Android View tag'и (как это делает ViewTreeLifecycleOwner или ViewTreeOnBackPressedDispatcherOwner). Раньше для такого паттерна была обязательна зависимость на compose-ui. Теперь только compose-runtime.Раньше чтобы из библиотеки достать значение из View tree, приходилось тянуть compose-ui ради LocalView и платформенные функции поиска по дереву View. Код жил в Android-сорсах, в commonMain такое не положишь:
// androidMain — compose-ui + Android-only API
val LocalMyOwner = compositionLocalOf<MyOwner?> { null }
@Composable
fun ProvideMyOwner(content: @Composable () -> Unit) {
val view = LocalView.current
val owner = remember(view) {
view.findViewTreeMyOwner()
}
CompositionLocalProvider(LocalMyOwner provides owner) {
content()
}
}
С новым API описание CompositionLocal живёт в commonMain и тянет только compose-runtime. Связку с View tree описывает ViewTreeHostDefaultKey, и хост сам отдаёт значение через своего провайдера:
// commonMain — только compose-runtime
val MyOwnerKey = ViewTreeHostDefaultKey<MyOwner>(
viewTagId = R.id.view_tree_my_owner
)
val LocalMyOwner = compositionLocalWithHostDefaultOf(MyOwnerKey)
// Использование — без изменений
@Composable
fun MyComponent() {
val owner = LocalMyOwner.current
// ...
}
Теперь авторы Compose библиотек могут держать определение CompositionLocal в общем модуле без зависимости на compose-ui. Профит - меньше транзитивных зависимостей в KMP-библиотеках и более чистое разделение слоёв runtime и UI.
#Compose #CMP
Please open Telegram to view this post
VIEW IN TELEGRAM