База знаний android
1 subscriber
2.48K photos
284 videos
2 files
3.77K links
Download Telegram
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/
🏝 Вышел Kotlin 2.4.0-Beta2. Стабильные Context parameters, UUID и explicit backing fields, но это не то, что волнует в первую очередь.

🔥Главное произошло в KMP части - Flows теперь экспортируются в Swift как AsyncSequence по умолчанию. Это значит, что вся эта суета с обёртками и callback-based API просто исчезает. Пишешь Flow на Kotlin-стороне, Swift видит AsyncSequence, готово.

🔥 Swift packages теперь импортируются прямо в Gradle без всех этих танцев с CocoaPods. Firebase, другие iOS либы импортируются через build.gradle.kts как обычные зависимости.

🚀 Для Native GC поменялся алгоритм GC c PMCS на CMS — это снижает паузы при сборке мусора. Особенно заметно будет в Compose Multiplatform приложениях.

🐘В JVM части Java 26 поддержка, в Gradle совместимость с 9.4.1.

🔥 Collection literals в экспериментальной стадии. Теперь не только в аннотациях. Сомнительно... ну ОКЭЙ!

🔗 Больше подробностей в документации и @kotlin_broadcast

#kotlin #kmp #swift #release
Please open Telegram to view this post
VIEW IN TELEGRAM
🏝 Поддержка литералов коллекций в Kotlin 2.4.0 [ЭКСПЕРИМЕНТАЛЬНО]

// Вместо
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
🏝🦢 Kotlin to Swift Export - экспериментальная поддержка конвертирования Kotlin Flow в Swift AsyncSequence

Теперь работать с 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
⚙️ Кажется, проблема с обёртками для Preview в Compose наконец-то решена нормально — без копипаста на каждый файл.

В 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 💬 Чат канала
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
⚙️ Отладка shared element анимаций в Compose 1.11 стала проще. Появилась функция LookaheadAnimationVisualDebugging, визуализирующая анимацию

#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
🤖 Styles API в Compose: революция в кастомизации UI

До недавнего времени стилизация компонентов в Compose делалась через параметры Composable функции (color = ..., padding = ...) или модификаторы. Это работало, но могло приводить к:
👉 избыточным рекомпозициям при смене состояний;
👉 дублированию кода для разных вариаций кнопок/карточек;
👉 сложностям с анимацией переходов между стилями.

Google представил Styles API (экспериментальный, о причинах такого статуса ниже) — новый подход, вдохновлённый CSS. Нужен Jetpack Compose 1.11.0 или свежее

Ключевые возможности
1️⃣ Производительность
Стили вычисляются на этапах layout и drawing, минуя composition. Это значит, что изменение стиля (например, при наведении) не вызывает рекомпозицию родительских компонентов.

2️⃣ Работа с состояниями
Без кучи if/else и animate*AsState. Просто описываете стиль для каждого состояния (нажат, сфокусирован, включён, ошибка и т.д.).

3️⃣ Встроенные анимации
Переходы между состояниями анимируются автоматически с физическими параметрами (демпфирование, жёсткость). Не нужно писать 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 плюсы очевидны!

💬 Пробовали уже играться со Styles API? Как вам идея отделить стили от композиции? Делитесь мнениями в комментариях 👇

#Compose #Material
Please open Telegram to view this post
VIEW IN TELEGRAM
🏝 HyperKey - Kotlin DSL для работы с ноткеями в Compose на Android и Desktop (чего iPadOS забыли)

HotKeyBox(
hotkeys = {
Key.S + Ctrl press { save() }
Key.K + Ctrl andThen Key.P press { openCommandPalette() }
Key.Space + Hyper press { spotlight() }
}
) {
MyApp()
}


#Kotlin #Android #Desktop
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 помогает и тут. Внутри анимация Grid, кастомная анимация волны на Canvas, изменение типа анимации tween -> spring, изменение animateContentSize
HotSwan обновляет примитивы в обход компиляции Gradle, за счет этого и получается такой результат
Forwarded from Android Broadcast
🤯 Те кто использовал Jetpack Paging 3 знают насколько он может справиться только с базовыми сценариямми. Шаг влево или вправо - уже боль!

Сегодня хочу познакомить вас с альтернативой — библиотекой 🐱 Paginator. Это KMP-решение для пагинации, которое решает те самые проблемы, где Paging 3 начинает "буксовать".

Paginator построен на отличной модели от Jetpack Paging 3: страница — это адресуемая ячейка в кэше, кэш — обычная структура данных, а навигация — обычные методы.

1️⃣ Адресуемые страницы
Прямые методы goNextPage(), goPreviousPage() и jump(Bookmark). Deeplink на сообщение из пуша решается одной строкой.

2️⃣ Мутации по запросу
MutablePaginator предоставляет CRUD-операции: replace { it.id == 42 }, removeWhere { it.deleted }. Один лайк меняет один элемент без инвалидации всей страницы.

3️⃣ Сохраняемое состояние
Кэш — обычная структура данных, которую можно сериализовать через kotlinx.serialization. Методы serializeToJson() и restoreFromJson() решают проблему Process Death без плясок с бубном.

4️⃣ Библиотека, а не фреймворк
Написан на чистом Kotlin без платформенных зависимостей и живёт в commonMain. Логика пагинации становится частью общего доменного слоя KMP, не копируется между платформами и не имеет двух реализаций.

5️⃣ Курсорная пагинация "из коробки"
Отдельный класс CursorPaginator с единой моделью состояний, транзакций и сериализации.

Paginator — это не обёртка над Paging 3. Это другая модель, переосмысливающая ключевые типы. Обёртка просто не смогла бы дать ни мутации элемента, ни сериализации кэша.


🔗 Оригинальная статья на Хабре
🐱 Репозиторий Paginator

#Android #AndroidDev #KMP
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Android Broadcast
🐘 В мире Android и тем более KMP проектов огромное количество зависимостей. Та самая ситуация, когда build зеленый, sync прошел, но приложение падает в рантайме из-за NoSuchMethodError или ClassNotFoundException, знакома многим. Причина — тихий конфликт версий. Gradle по умолчанию старается брать самую новую версию из всех найденных, но срабатывает не всегда. В разных модулях одного проекта могут спокойно жить разные версии одной библиотеки (например, okhttp 4.9.0 в модуле А и 4.11.0 в модуле Б). Gradle не считает это конфликтом, потому что модули изолированы. В рантайме при передаче объекта между модулями — ClassCastException. Особенно больно это бьет в KMP, где общая бизнес-логика связывает всё в единую цепочку.

Плагин 🐱 Dependency Conflict Analyzer переворачивает подход. Он встраивается в Gradle и каждый раз при синхронизации автоматически анализирует весь граф зависимостей по всем модулям. Не нужно ничего запускать вручную или гадать, кто что подтянул. Если есть расхождение в major-версиях — плагин сразу покажет конфликт в консоли. Причём он найдет даже скрытые расхождения между разными модулями, которые Gradle игнорирует.

# Пример работы плагина
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
🤖 GPT 5.5 и 5.4 лучше подходят для Android разработки, чем модели Claude. РАУНД!

Данные официального бенчмарка 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, эмуляторы, запуск билдов ... )

Ни для чего другого, кроме как мобильной разработке, это не подходит, так что поставить точно рекомендую!
This media is not supported in your browser
VIEW IN TELEGRAM
Все идет по пути к тому что у каждого тула будет CLI версия. Обсидиан запустил пару месяцев назад, а вот и Google (Пере)представляет Android CLI

По ощущениям оч кастрированный и является надстройкой над adb

(c) Антоха Разраб про AI
Forwarded from Android Good Reads
Если вы используете Metro, в качестве DI, то знайте что оно стало стабильным. Свеженаписанный легковесный DI для android

Релиз ноутс: https://github.com/ZacSweers/metro/releases/tag/1.0.0
Forwarded from Android Good Reads
Что нового в Compose в Апреле

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 9

Android Good Reads
Forwarded from Android Good Reads
Что нового в Compose в Апреле Часть 2

Экспериментальные API:

👉 Styles API: декларативные стили компонентов с автоматическими анимациями. Судя по бенчмаркам, быстрее модификаторов
👉 MediaQuery: адаптация под окружение (размер окна, тип клавиатуры, режим стола и т.д.)
👉 Grid: 2D-раскладка с треками, фракциями (fr) и авто-размерами
👉 FlexBox: мощный контейнер с гибким распределением пространства, wrap'ом и выравниванием. Замена Row/Column/Flow
👉 Новый SlotTable: ускорение случайных правок в дереве композиции (пока опционально через флаг)

Android Good Reads
Forwarded from Compose Broadcast
⚙️ Compose 1.11: Composition host defaults — мелочь, важная для KMP

В апрельском релизе 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