Android Good Reads
4.04K subscribers
354 photos
15 videos
1 file
2.08K links
Самые интересные статьи, видео и новости, связанные с Android разработкой. Не больше трёх материалов в день.

Автор канала: @Lamprof

Размещение рекламы: @tanyasanovna
Download Telegram
🚀 Большие релизы предыдущей недели. Выделяем время на миграцию!

👉 Ktor 3.0 Миграция на kotlinx-io, немного улучшений перфоманса, поддержка WASM по всем направлениям. Миграция не сложная

👉 Фиксы и немного стабильности в Kotlin 2.0.21. Теперь поддерживается Xcode 16, а для тех кто любит быть на острие технологий Kotlin 2.1.0-Beta2. Страничка со всеми изменениями тут, но в кратце:
⚡️Условия в when
⚡️break и continue в лямбдах
⚡️улучшения в составлении строковых переменных
⚡️Улучшение k2 kapt (все еще в альфе)
⚡️AGP минимальная теперь 7.3.1, а Gradle 7.6.1
⚡️Большое количество улучшений для WASM и компилятора

👉 Compose Multiplatform 1.7.0. Тут нас ждёт compile-time safety для навграфа, огромные улучшения по перформансу для iOS. Мы с командой уже заценили, хоть и не все было гладко на предрелизных версиях. Material3 библиотеки постепенно мигрируют в общую кодовую базу. Уже привычный Shared element transitions тоже тут!
Please open Telegram to view this post
VIEW IN TELEGRAM
Все чаще мне попадаются статьи с отказом от DI фреймворков. Пока что не видел какого-либо жизнеспособного решения в продакшене без DI.
Автор одной из минималистичных DI библиотек - Magnet тоже предлагает отказаться от DI и использовать явные зависимости. И почему же?
👉 Это сложно
👉 Время сборки сильно вырастает
👉 Время холодного старта тоже увеличивается
👉 Часто обьекты графа остаются в памяти дольше чем нужно. Да, большинство команд просто плодит синглтоны в том или ином виде и инжектит их. Увы, это и правда плохой подход

Взамен предлагается подход, кратно увеличивающий количество обслуживающего кода, однако использующий ручное и явное внедрение зависимостей:

interface NetworkModule {
val httpClient: HttpClient

class Default : NetworkModule {
override val httpClient: HttpClient by lazy ...
}
}

interface DatabaseModule {
val database: Database

class Default : DatabaseModule {
override val database: Database by lazy ...
}
}

interface RssReaderModule {
fun fetchRss(): FetchRss

class Default(
private val networkModule: NetworkModule,
private val databaseModule: DatabaseModule,
) : UserModule {
override fun fetchRss(): FetchRss ...
}
}

Больше примеров внутри статьи

Таким образом, мы получим достаточно прозрачную систему внедрения зависимостей в проекте, где нет никакой магии и у нас в руках полный контроль над всеми обьектами, однако масштабируемость этого подхода вызывает вопросы. Что вы думаете про тренд отказа от DI?
Преиспользуем стили в Jetpack Compose

Чтобы сотню раз не проставлять паддинги и обводки в дизайн/системе, объединяем в простой универсальный Modifier:


fun Modifier.paddedBorder(): Modifier {
return this
.border(1.dp, Color.Black)
.padding(8.dp)
}

fun Modifier.cardStyle(): Modifier {
val shape = RoundedCornerShape(4.dp)

return this
.shadow(4.dp, shape)
.background(Color.White)
}


И несколько предупреждений:
👉 Не нужно паковать весь интерфейс внутрь одной функции, сохраняйте атомарность и переиспользуемость таких модификаторов
👉 Не делайте слишком глубокую вложенность из таких модификаторов. Это намного сложнее исправлять
По мере роста проекта возникает всё больше и больше проблем с производительностью UI. Что-то мы отлавливаем автоматически на пулреквестах, что то по репортам пользователей. О том как от релиза к релизу найти снижение производительности в Compose элементах и пишет автор статьи

👉 Берем Macrobenchmark (запуск сценариев), Perfetto (анализ и визуализация трассировок) и Diffetto (сравнения трассировок и определение что за элемент деградировал)

👉 Последовательно перекладываем данные из инструмента в инструмент и находим источник. В статье пример на маленьком приложении со сценарием замедления и тем как смотреть метрики

👉 В больших проектах множество @Composable элементов, тут то Diffeto и выступает незаменимым помошником для фильтрации огромного количества трассировок

На мой взгляд, такой пошаговый анализ хорошо автоматизируется и может быть использован в качестве дополнительной проверки перед мержем задачки в главную ветку.
⭐️ А вот и порт easycrop для Compose Multiplatform подъехал! В своей статье автор делится мотивацией и проделанными изменениями. Удобная работа с изображением на всех платформах!

Автор просит об обратной связи по поводу статьи и библиотеки. Не стесняйтесь задавать вопросы в комментариях!
Please open Telegram to view this post
VIEW IN TELEGRAM
Koin Annotations 1.4 в Compose Multiplatform

Зачем использвать:

👉 Код чище: аннотации обеспечивают декларативный и краткий способ определения зависимостей
👉 Поддержка IDE: проще навигация по коду и рефакторинг
👉 Проверка во время компиляции: более раннее обнаружение проблем с внедрением зависимостей (Немного жертвуем скоростью сборки)
👉 Убираем обслуживающий код: KSP автоматически генерирует необходимые extension функции
👉 Multiplatform поддерживается!

Документация: https://insert-koin.io/docs/reference/koin-annotations/start/
Выглядит хорошей возможностью для масштабирования koin как DI в проекте
Параметризированные Android тесты с Burst 2.0

👉 Писать параметризированные тесты проще и аккуратнее
👉 Android и мультиплатформа поддерживаются из коробки
👉 Проще запускать на большом количестве устройств, решены проблемы с TestParameterInjector
😡 К сожалению, фреймворк не очень хорошо работает с Android Studio, что может осложнить взаимодействие с ним
🤨 Минимальная версия Kotlin 2.0.21, которая прокидывается транзитивно

Пример:
@Test
fun drinkSoda(
soda: String = burstValues("Pepsi", "Coke"),
ice: Boolean,
distribution: Distribution,
) {
...
}


⭐️ 113+ https://github.com/cashapp/burst
Please open Telegram to view this post
VIEW IN TELEGRAM
Kotlin Multiplatform Roadmap 2025

Дорожная карта на 2025 год. Как будет развиваться мультиплатформа:
👉 В Compose Multiplatform будет упор на интеграцию Jetpack Compose, улучшение перфоманса iOS. Стабилизация и доведение до релизного состояния таких фичей как навигация, управление ресурсами и переводы, а так же accessibility!
👉 Kotlin-to-Swift экспорт, вместо существующего проблемного Kotlin-to-Objective-C . Первую публичную версию ожидаем в 2025 году.
👉 Больше интеграций с Android Studio для удобной работы. Обещают отдельную KMP IDE базирующуюся на существующей IDE Fleet. Очень хочется попробовать, мы в команде иногда используем Fleet, но не как полноценную IDE, а как вспомогательный инструмент
👉 Развитие экосистемы библиотек для KMP. Очень схоже с тем, чем планирует заниматься и Kotlin команда
👉 Amper и его улучшения, как инструмента для сборки. Пока что выглядит сыро, но обещают ряд доработок в особенности для Compose Multiplatform. Gradle, конечно, плох, а bazel сложен для малых команд, но это вдоль и поперек исследованные инструменты. Amper будет сложно конкурировать, но некоторые сценарии просто невозможно реализовать без глубокой интеграции в KMP (ex. Фуллстак приложение на KMP с бекендом)
👉 Продолжат работать над Gradle и другими инструментами для сборки приложений. Обещают упрощение подключения зависимостей для KMP проектов
🚀 Следующая версия Android Studio это Meerkat
Основные фичи все еще не заявлены, но уже есть ряд исправлений по сравнению с предыдущими версиями
Please open Telegram to view this post
VIEW IN TELEGRAM
PDD - Preview Driven Development

👉 Snapshot Testing лежит в основе. Есть много инструментов помогающих в реализации, начиная от встроенных, заканчивая paparazzi и облачными решениями
👉 Запускаем каждый отдельный экран или часть, как Сomposable функци в превью внутри нашего окружения. Автор мокает весь уровень данных, что позволяет включить в тестирование viewModel и увидеть реально возможное поведение приложения
👉 В каждом пулреквесте вы видите какие части приложения были затронуты вашими изменениями
👉 Для автоматизации сразу же предлагается Github Action и Gradle плагин с глубокой интеграцией в один из облачных инструментов

Еще чуть чуть про PDD было вот тут и на Droidcon тут
Как лямды ломают хэширование в data class

Надеюсь, вы не кладете лямды напрямую в data class, а если кладете, то делаете это правильно!


val detective1 = DetectiveDataClass(
name = "Sherlock",
age = 40,
alias = "Holmes",
onDetectiveAlert = { println("Elementary!") }
)

val detective2 = DetectiveDataClass(
name = "Sherlock",
age = 40,
alias = "Holmes",
onDetectiveAlert = { println("Elementary!") }
)

println(detective1 == detective2)
// false, хотя ожидается true

println(detective1.hashCode() == detective2.hashCode())
// false, хотя ожидается true

Сравнивая два одинаковых data class’а, получаем неожиданный результат

А исправить можно вот так:
👉 Переопределяем equals и hashCode для этого класса. Не оптимально
👉 Используем method reference

val detective1 = DetectiveDataClass(
name = "Sherlock",
age = 40,
alias = "Holmes",
onDetectiveAlert = ::alertFunction
)

👉 Используем интерфейс и передаем полноценный объект внутрь

Лучше всего, конечно, вынести лямду куда подальше от data class
Android 16 Preview BAKLAVA

Вроде, только-только Android 15 проводили в релиз, а уже выходит следующая версия. План такой: Превью сегодня ➡️ Бета в Январе ➡️Стабильная бета в Марте ➡️ релиз по готовности (Июнь)

👉 Изменения в API можно глянуть тут
👉 Внутри релиза теперь есть разделение на минорный и мажорный:

val minorSdkVersion = Build.getMinorSdkVersion(VERSION_CODES_FULL.BAKLAVA)

👉 Новый встроенный photo picker.
👉 Немного интеграции с Health Connect
👉 Privacy Sandbox

По ощущениям, работы, с релизом Privacy Sandbox, прибавится у всех Android разработчиков. А вы что думаете?
Kotlin 2.1.0-RC2

Внутри много доработок, но самая важная для меня - стабильная работа в xCode 16+. Я, по-глупости, обновился на macOS Sequoia месяц назад, что потянуло за собой новый xCode, и iOS сборка нашего KMM проекта сломалась на моей ноутбуке.

Да, всегда можно указать xCode через xcodes в командной строке, но потеря возможности манипулировать нативной частью приложения сильно ограничивала меня. Beta версии 2.1.0 продолжили сыпать ошибками в наш проект, а вот RC2 наконец-таки решил все проблемы и все стало как раньше!

Вывод: Не забывайте отключать автообновления 😅
Тестируйте умнее, а не усерднее
Google обновили документацию по тестированию приложений

👉 Акцент на продуктивности разработки. Пишем тесты пропорционально пирамиде тестирования, не переусердствуем с частотой запуска тестов. Все ради минимизации стоимости запуска и написания тестов. Если вы все еще плаваете в терминологии и не отличаете где интеграционные, а где Unit тесты, то внутри есть примеры
👉 Дополнили рекомендации по популярным ныне видам тестирования: Скриншот тестирование (недавно разбирали тут), тестирование производительности
👉 Новый термин для UI тестов, не делающих скриншоты, - поведенческое тестирование
👉 Несколько рекомендаций для больших тестов и robolectric
👉 В связи с распространение Foldable девайсов с нестандартным разрешением. Рекомендации по их тестированию, а так же инструменты которые вам в этом помогут
Android Good Reads
Android 16 Preview BAKLAVA Вроде, только-только Android 15 проводили в релиз, а уже выходит следующая версия. План такой: Превью сегодня ➡️ Бета в Январе ➡️Стабильная бета в Марте ➡️ релиз по готовности (Июнь) 👉 Изменения в API можно глянуть тут 👉 Внутри…
Касательно Android 16 DP1, это ведь первый релиз по новой системе ускоренных релизов, верно?

Согласно плану, нас ждёт много нового в средине и в конце года. Новое API и новые Deprecated 😅. Заранее предупреждают, что лучше настроить CI так, чтобы она гоняла тесты на разных API levels. Кстати, а какой targetSdk у вас сейчас в проекте, следуете рекомендациям от гугла?
Полезная библиотека этой недели - https://strikt.io/ (563 ⭐️)
Устали от assertTrue(true == true)? Тогда strikt может упростить вам жизнь!

Примеры:
// Flexible assertions about collections
val subject = listOf("Eris", "Thor", "Anubis", "Ra")
expectThat(subject)
.contains("Eris", "Thor", "Anubis")

// “Narrow” the assertion to elements or ranges
expectThat(subject)[0].isEqualTo("Eris")

// Make grouping assertions
val subject = Deity.values().map { it.toString() }
expectThat(subject)
.isNotEmpty()
.any { startsWith("E") }

// Custom assertions are extension functions
fun Assertion.Builder<LocalDate>.isStTibsDay() =
assert("is St. Tib's Day") {
when (MonthDay.from(it)) {
MonthDay.of(2, 29) -> pass()
else -> fail()
}
}
expectThat(LocalDate.of(2020, 2, 29)).isStTibsDay()


Сильно гибче стандартного функционала. Работает с JUnit5, Minutest и Spek
Please open Telegram to view this post
VIEW IN TELEGRAM
Android Good Reads
Kotlin 2.1.0-RC2 Внутри много доработок, но самая важная для меня - стабильная работа в xCode 16+. Я, по-глупости, обновился на macOS Sequoia месяц назад, что потянуло за собой новый xCode, и iOS сборка нашего KMM проекта сломалась на моей ноутбуке. Да…
А вот и релиз!
Кратко:

👉 Новые экспериментальные фичи. Guard условие в when, break и continue не только для своего скоупа. Разбирали частично ---> тут
👉 Обновили K2 компилятор и сделали ряд улучшенией в kapt
👉 Для KMM стабилизировали Gradle DSL, много улучшений для Kotlin/Native, Kotlin/Wasm
👉 Поддержка последних версий Android Gradle Plugin
👉 Обновили документацию

Полный список изменений тут
А еще сегодня (28.11) в 7 вечера будет прямая трансляция с разработчиками на тему этого обновления тут
👨‍💻 Пропала мотивация решать литкод каждый день? Тогда на декабрь забирайте Advent Of Code от Kotlin!
Хорошая тренировка вместо одинаковых задач на литкоде. А если ваши знакомые хотят попробовать Kotlin, то и отличный интенсив для них

Решаете свой advent of code и хотите посоревноваться? Кидайте ссылки в комменты!
Please open Telegram to view this post
VIEW IN TELEGRAM
Ультимативный гайд по написанию чисто кода на Jetpack Compose

Тут приведу основные идеи для @Composable функции, а то как надо и как не надо можно увидеть внутри статьи:

👉 Именование функций должно быть в PascalCase
👉 Последовательность параметров
Официальная рекомендация Android: Обязательные параметры -> Modifier -> необязательные параметры -> Вложенная @Composable
Более удобная версия: Modifier -> входные данные -> UI параметры -> калбеки -> Вложенная @Composable
👉 Либо возвращаем значение из @Composable, либо что-то рисуем. Но не одновременно
👉 У каждой функции не более 1 лайаута и при этом она должна быть независимой от места вызова
👉 Каждая функция должна содержать единственный Modifier со стандартным значением
👉 Старайтесь не держать собственное состояние внутри каждой @Composable функции
👉 Используйте отступы предоставляемые через Scaffold

Примеры как надо и как не надо читаем тут
Android Good Reads
А вот и релиз! Кратко: 👉 Новые экспериментальные фичи. Guard условие в when, break и continue не только для своего скоупа. Разбирали частично ---> тут 👉 Обновили K2 компилятор и сделали ряд улучшенией в kapt 👉 Для KMM стабилизировали Gradle DSL, много улучшений…
И еще раз про Guard Conditions

👉 Как включить:
1) Проверяем что стоит галочка напротив Enable K2 mode в IDE
2) Добавляем compileOptions

kotlin {
compilerOptions {
freeCompilerArgs.add("-Xwhen-guards")
}
}


👉 Без guard conditions:

when (response) {
is HttpResult.Success -> println("Success")
is HttpResult.Failed -> {
if (response.statusCode == 503) {
println("Maintenance")
} else {
println("Failed with code ${response.statusCode}")
}
}
}


👉 С guard conditions:

when (response) {
is HttpResult.Success -> println("Success")
is HttpResult.Failed if response.statusCode == 503 -> println("Maintenance")
is HttpResult.Failed -> println("Failed with code ${response.statusCode}")
}