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

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

Размещение рекламы: @tanyasanovna
Download Telegram
Абстракция для работы со строками

Возникали вопросы как работать со строками в Compose на UI и на data слое чтоб это выглядело удобно? А еще чтобы строки с разных источников были закрыты общей абстракцией и не нужно было разделять строки от пользователя, string.xml и пришедшие с бекенда

Автор предлагает воспользоваться sealed interface такого вида:
sealed interface StringHolder {
// These are the 2 essential Holders
data class Value(val value: String) : StringHolder
data class Resource(@StringRes val resId: Int) : StringHolder

// These are additional and will come in handy!
class ParametrizedResource(@StringRes val resId: Int, vararg val formatArgs: Any) : StringHolder
class Plural(@PluralsRes val resId: Int, val count: Int, vararg val formatArgs: Any) : StringHolder
}


Пример использования:
@Composable
fun ExampleText(stringHolder: StringHolder) {
Text(text = stringHolder.value)
}

class MessageDataSource(private val context: Context, private val api: Api) {
suspend fun sendMessage(message: StringHolder) {
api.sendMessage(message.fromContext(context))
}
}



Из минусов - конечно сложновато это адаптировать к CMP, да и протекание логики в UI может не соответствовать вашей архитектуре.
👍7👎4🔥1💯1
Android Good Reads
Как Uber мигрирует нетворк слой на gRPC Знакомая боль всех мобильный разработчиков с миграцией чего либо на слое данных. Как решает проблему Uber: 👉 Protocol Buffers вместо JSON, HTTP/2 без откатов к HTTP/1.1 👉 Использование gRPC, на который мигрировали…
А вот и демонстрация Kotlin RPC. На примере создания фуллстак приложения с KMM, Koin, Wasm
Самый приятный момент это в том что клиент и сервер шарят интерфейсы, модели и статусы. В самой статье много кода, так рекомендую взглянуть.
Из болезненного, со слов автора, это WASM, который еще в альфе, и высокий порог входа. Нужно быть чуть знакомым с kmm концепцией, чтобы разобраться в этом.
🔥61👍1👏1
🚀 Большие релизы предыдущей недели. Выделяем время на миграцию!

👉 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
🔥8👏21
Все чаще мне попадаются статьи с отказом от 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?
🔥11👎10🥴8👍6🤔3😁2
Преиспользуем стили в 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)
}


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

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

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

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

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

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

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

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

Документация: https://insert-koin.io/docs/reference/koin-annotations/start/
Выглядит хорошей возможностью для масштабирования koin как DI в проекте
👍9🔥2
Параметризированные 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
👍3🔥1😨1
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 проектов
👍7👎3🔥3🥱2🥴1
🚀 Следующая версия Android Studio это Meerkat
Основные фичи все еще не заявлены, но уже есть ряд исправлений по сравнению с предыдущими версиями
Please open Telegram to view this post
VIEW IN TELEGRAM
👍31🔥1
PDD - Preview Driven Development

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

Еще чуть чуть про PDD было вот тут и на Droidcon тут
👍5🎉2🙈21🤣1
Как лямды ломают хэширование в 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
👍23🔥65🗿3
Android 16 Preview BAKLAVA

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

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

val minorSdkVersion = Build.getMinorSdkVersion(VERSION_CODES_FULL.BAKLAVA)

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

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

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

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

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

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

Согласно плану, нас ждёт много нового в средине и в конце года. Новое API и новые Deprecated 😅. Заранее предупреждают, что лучше настроить CI так, чтобы она гоняла тесты на разных API levels. Кстати, а какой targetSdk у вас сейчас в проекте, следуете рекомендациям от гугла?
👍41🔥1
Полезная библиотека этой недели - 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
🔥43👍1💯1
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 вечера будет прямая трансляция с разработчиками на тему этого обновления тут
👍6👏3🔥1
👨‍💻 Пропала мотивация решать литкод каждый день? Тогда на декабрь забирайте Advent Of Code от Kotlin!
Хорошая тренировка вместо одинаковых задач на литкоде. А если ваши знакомые хотят попробовать Kotlin, то и отличный интенсив для них

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