Android Good Reads
Kotlin 2.1.0-RC2 Внутри много доработок, но самая важная для меня - стабильная работа в xCode 16+. Я, по-глупости, обновился на macOS Sequoia месяц назад, что потянуло за собой новый xCode, и iOS сборка нашего KMM проекта сломалась на моей ноутбуке. Да…
А вот и релиз!
Кратко:
👉 Новые экспериментальные фичи. Guard условие в
👉 Обновили K2 компилятор и сделали ряд улучшенией в
👉 Для KMM стабилизировали Gradle DSL, много улучшений для Kotlin/Native, Kotlin/Wasm
👉 Поддержка последних версий Android Gradle Plugin
👉 Обновили документацию
Полный список изменений тут
А еще сегодня (28.11) в 7 вечера будет прямая трансляция с разработчиками на тему этого обновления тут
Кратко:
👉 Новые экспериментальные фичи. Guard условие в
when
, break
и continue
не только для своего скоупа. Разбирали частично ---> тут👉 Обновили K2 компилятор и сделали ряд улучшенией в
kapt
👉 Для KMM стабилизировали Gradle DSL, много улучшений для Kotlin/Native, Kotlin/Wasm
👉 Поддержка последних версий Android Gradle Plugin
👉 Обновили документацию
Полный список изменений тут
А еще сегодня (28.11) в 7 вечера будет прямая трансляция с разработчиками на тему этого обновления тут
The JetBrains Blog
Kotlin 2.1.0 Released | The Kotlin Blog
The Kotlin 2.1.0 release is out! This version introduces new language features, tooling updates, performance improvements, and important fixes. Learn more!
Хорошая тренировка вместо одинаковых задач на литкоде. А если ваши знакомые хотят попробовать Kotlin, то и отличный интенсив для них
Решаете свой advent of code и хотите посоревноваться? Кидайте ссылки в комменты!
Please open Telegram to view this post
VIEW IN TELEGRAM
Ультимативный гайд по написанию чисто кода на Jetpack Compose
Тут приведу основные идеи для
👉 Именование функций должно быть в PascalCase
👉 Последовательность параметров
Официальная рекомендация Android: Обязательные параметры ->
Более удобная версия:
👉 Либо возвращаем значение из
👉 У каждой функции не более 1 лайаута и при этом она должна быть независимой от места вызова
👉 Каждая функция должна содержать единственный
👉 Старайтесь не держать собственное состояние внутри каждой
👉 Используйте отступы предоставляемые через
Примеры как надо ✅ и как не надо ❌ читаем тут
Тут приведу основные идеи для
@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) Проверяем что стоит галочка напротив
2) Добавляем
👉 Без guard conditions:
👉 С guard conditions:
👉 Как включить:
1) Проверяем что стоит галочка напротив
Enable K2 mode
в IDE2) Добавляем
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}")
}
JetBrains исследует возможность добавить Hot Reload в Compose
https://github.com/JetBrains/compose-hot-reload (404⭐️ )
Мультиплатформа тоже поддерживается, судя по всему. Для сборки используеть отдельная версия официальных плагинов (
5 минутное видео с демонстрацией от одного из разработчиков
https://github.com/JetBrains/compose-hot-reload (404
Мультиплатформа тоже поддерживается, судя по всему. Для сборки используеть отдельная версия официальных плагинов (
2.1.0-firework.31
) и сам плагин для hot-reload
5 минутное видео с демонстрацией от одного из разработчиков
Please open Telegram to view this post
VIEW IN TELEGRAM
MVI + Jetpack Compose что и как!
Внутри статьи немного теории о том как устроен
👉
👉 Compose навигация
Справедливо заметили, что при таком подходе,
Финальный вид приложения можно посмотреть на GitHub
Внутри статьи немного теории о том как устроен
MVI
(Model
- View
- Intent
) и как он работает.👉
Hilt
, KSP
, Room
и ViewModel
как основа для проекта👉 Compose навигация
Справедливо заметили, что при таком подходе,
ViewModel
должна отвечать только за управление состоянием, а разруливание корутин должно уйти на слой ниже, в репозиторий. Финальный вид приложения можно посмотреть на GitHub
Ищем блокирующие вызовы из неблокирующих потоков
Проверяем приложение на наличие блокирующих вызовов. Они могут вызывать микрофриз, а не краш приложения. С библиотекой BlackHound (1400⭐️ +) вы получите полноценной краш-репорт. Используется вместе с отладочной тулзой для корутин:
Пример:
Краш:
Фикс:
Проверяем приложение на наличие блокирующих вызовов. Они могут вызывать микрофриз, а не краш приложения. С библиотекой BlackHound (1400
org.jetbrains.kotlinx:kotlinx-coroutines-debug
Пример:
import reactor.blockhound.BlockHound
import kotlinx.coroutines.debug.CoroutinesBlockHoundIntegration
import kotlinx.coroutines.*
fun main() {
BlockHound.install(CoroutinesBlockHoundIntegration())
runBlocking {
launch(Dispatchers.Default) {
Thread.sleep(1000) // Краш
}
}
}
Краш:
Exception in thread "main" reactor.blockhound.BlockingOperationError: Blocking call! java.lang.Thread.sleep
at java.base/java.lang.Thread.sleep(Thread.java)
at MainKt$main$1$1.invokeSuspend(Main.kt:12)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)
Фикс:
import reactor.blockhound.BlockHound
import kotlinx.coroutines.debug.CoroutinesBlockHoundIntegration
import kotlinx.coroutines.*
fun main() {
BlockHound.install(CoroutinesBlockHoundIntegration())
val droidgr = Dispatchers.IO.limitedParallelism(10) // или просто Dispatchers.IO
runBlocking {
launch(droidgr) {
Thread.sleep(1000) // OK
}
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
Записи докладов с droidcon London 2024 про Compose:
- A Snapshot Preview of Paparazzi 2.0
- Building State Holders in Compose with Molecule: A New Approach to Reusable UI Components
- Crafting Narratives: Shaping TalkBack with Compose Semantics
- Creating a Custom Compose Layout, Step-by-Step
- Cutting-Edge-to-Edge in Android 15: Using Previews and Testing in Jetpack Compose to Manage Insets
- Deep Dive into the Compose Compiler
- Designing scalable Compose APIs
- Hardware development with KMP and Compose Desktop
- Jetpack Compose: Drawing without pain and recomposition
- Navigation in a Multiplatform World: Choosing the Right Framework for your App
- Personalizing Accessibility
- Project Sparkles: how Compose is changing Android Studio
- Streamlining Permission Request in Jetpack Compose
- Tap it! Shake it! Fling it! Sheep it! - The Gesture Animations Dance!
- Text in Compose: Beyond the Basics
- What’s New in Compose Multiplatform - A Live Tour
- Why is adaptive layout a nightmare?
- A Snapshot Preview of Paparazzi 2.0
- Building State Holders in Compose with Molecule: A New Approach to Reusable UI Components
- Crafting Narratives: Shaping TalkBack with Compose Semantics
- Creating a Custom Compose Layout, Step-by-Step
- Cutting-Edge-to-Edge in Android 15: Using Previews and Testing in Jetpack Compose to Manage Insets
- Deep Dive into the Compose Compiler
- Designing scalable Compose APIs
- Hardware development with KMP and Compose Desktop
- Jetpack Compose: Drawing without pain and recomposition
- Navigation in a Multiplatform World: Choosing the Right Framework for your App
- Personalizing Accessibility
- Project Sparkles: how Compose is changing Android Studio
- Streamlining Permission Request in Jetpack Compose
- Tap it! Shake it! Fling it! Sheep it! - The Gesture Animations Dance!
- Text in Compose: Beyond the Basics
- What’s New in Compose Multiplatform - A Live Tour
- Why is adaptive layout a nightmare?
Начиная с версии Android 16 гугл планирует сокращать User-Agent
Итоговое значение будет выглядеть вот так:
Итоговое значение будет выглядеть вот так:
Mozilla/5.0 (Linux; Android 10; K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.000 Mobile Safari/537.36
This media is not supported in your browser
VIEW IN TELEGRAM
Android XR SDK Developer Preview
Платформа для расширенной реальности теперь и на Android. Базируется так же, на open source - OpenXR. Все приложения на Android должны быть совместимы
Пощупать в эмуляторах можно тут, доступно для разработки начиная с Android Studio Meerkat
Платформа для расширенной реальности теперь и на Android. Базируется так же, на open source - OpenXR. Все приложения на Android должны быть совместимы
Пощупать в эмуляторах можно тут, доступно для разработки начиная с Android Studio Meerkat
О разработке SDK
Что нужно помнить при работе с SDK, как делать не нужно:
👉 Разработка SDK != разработка клиентского приложения. Например, не стоит перехватывать UI у клиентского приложения, если внутри SDK возникла ошибка. Лучше, прокиньте состояние ошибки и дайте пользователям SDK самим решать как ее отобразить.
👉 Предоставление пользовательского интерфейса через SDK влечет за собой ряд проблем: Отсутствие гибкости, конфликты ресурсов, увеличение размера SDK и сложности в его обслуживании, а так же проблемы масштабируемости. Но если вам все-таки необходимо поставлять UI с SDK, то разделите свое SDK на 2 разных
👉 Минимизируйте внешние зависимости. А если используете - то лучше всего их изолировать. Неприятно ради 1 библиотеки подключать RxJava и Lottie, для конвертации в свой формат. Не забывайте, что не все библиотеки предоставляют лицензию на использование в коммерческих целях, например, GPL лицензии
❗️ Старайтесь избегать глобальных синглтонов. Неприятно, когда в приложении появляется утечка памяти, а я никак не могу на это повлиять, кроме как отказаться от вашего SDK.
👉 Пишите документацию и CHANGELOG
С какими неприятными последствиями спорных решений в SDK вы сталкивались?
Что нужно помнить при работе с SDK, как делать не нужно:
👉 Разработка SDK != разработка клиентского приложения. Например, не стоит перехватывать UI у клиентского приложения, если внутри SDK возникла ошибка. Лучше, прокиньте состояние ошибки и дайте пользователям SDK самим решать как ее отобразить.
👉 Предоставление пользовательского интерфейса через SDK влечет за собой ряд проблем: Отсутствие гибкости, конфликты ресурсов, увеличение размера SDK и сложности в его обслуживании, а так же проблемы масштабируемости. Но если вам все-таки необходимо поставлять UI с SDK, то разделите свое SDK на 2 разных
package
: UI и Core Logic с возможностью их раздельного использования. Не забудьте про Analytics Hooks в UI модуле!👉 Минимизируйте внешние зависимости. А если используете - то лучше всего их изолировать. Неприятно ради 1 библиотеки подключать RxJava и Lottie, для конвертации в свой формат. Не забывайте, что не все библиотеки предоставляют лицензию на использование в коммерческих целях, например, GPL лицензии
👉 Пишите документацию и CHANGELOG
С какими неприятными последствиями спорных решений в SDK вы сталкивались?
Please open Telegram to view this post
VIEW IN TELEGRAM
Medium
SDK Development; The Good, The Bad, The Ugly
Or, How to avoid death threats from angry developers
Анонс Koin IDE Plugin
Что позволит делать плагин:
👉 Видеть все дерево зависимостей
👉 Навигация между компонентами
Валидация на уровне Koin делается в рантайме, но через плагин можно проверить валидность состояния с помощью статического анализа. Предварительная дата релиза - 1 квартал 2025
Что позволит делать плагин:
👉 Видеть все дерево зависимостей
👉 Навигация между компонентами
Валидация на уровне Koin делается в рантайме, но через плагин можно проверить валидность состояния с помощью статического анализа. Предварительная дата релиза - 1 квартал 2025
blog.kotzilla.io
Koin IDE Plugin: A Koin Configuration Tree
Introducing the Koin IDE Plugin for Kotlin: See configurations, navigate dependencies, & validate your Koin setup an intuitive tree view. Coming Q1 2025.
Разбираем Low Memory Management в Android: Kswapd & LMK
👉 Память в системе разделяется на RAM, zRAM и Storage(постоянная)
👉 RAM разделяется на Pages по 4KB. Каждый Page может быть used или unused.
used в свою очередь делится на Private, если им владеет только 1 процесс или Shared в ином случае
Так же used Page может быть Clean, если это копия данных из Storage и Dirty если это модифицированные данные
Само управление происходит с помощью двух механизмов:
👉
👉
Чуть подробнее про LMK внутри статьи или в этом видео
👉 Память в системе разделяется на RAM, zRAM и Storage(постоянная)
👉 RAM разделяется на Pages по 4KB. Каждый Page может быть used или unused.
used в свою очередь делится на Private, если им владеет только 1 процесс или Shared в ином случае
Так же used Page может быть Clean, если это копия данных из Storage и Dirty если это модифицированные данные
Само управление происходит с помощью двух механизмов:
👉
kswapd
(kernel swap daemon) Удаляет Clean Pages и восстанавливает копию из хранилища. Сжимает и перемещает Dirty Pages в zRAM. Активируется при нехватки памяти и возвращает все как было, когда процесс, владеющей Page снова оживает.👉
LMK
(Low-memory killer) Удаляет процессы в порядке с приложенной таблицы, тем самым освобождая Pages ими владеющие.Чуть подробнее про LMK внутри статьи или в этом видео
Android Good Reads
Android XR SDK Developer Preview Платформа для расширенной реальности теперь и на Android. Базируется так же, на open source - OpenXR. Все приложения на Android должны быть совместимы Пощупать в эмуляторах можно тут, доступно для разработки начиная с Android…
Android XR KMP
Добавить поддержку Android XR в ваше KMP приложение - легко! Две составляющих:
👉 Добавить зависимость
👉 Добавить
iOS имплементация игнорируется, но предположу, что жизненные циклы достаточно сильно различаются чтобы вписать обе VR/AR платформы сразу
Добавить поддержку Android XR в ваше KMP приложение - легко! Две составляющих:
👉 Добавить зависимость
// shared/build.gradle.kts
sourceSets {
androidMain.dependencies {
implementation(libs.androidx.xr.compose)
implementation(libs.androidx.xr.compose.material3)
implementation(libs.androidx.xr.scenecore)
}
}
👉 Добавить
expect
/ actual
интерфейсыiOS имплементация игнорируется, но предположу, что жизненные циклы достаточно сильно различаются чтобы вписать обе VR/AR платформы сразу
Цикл статей про Mockk и тесты
Не все согласны, что это хороший подход, но существующий и практикующий многими компаниями:
👉 Mockk и основы
👉 Mockk, продвинутое использование. Мокаем статик методы, синглтоны и
👉 Unit-тестирование Android компонентов
👉 Мокаем suspend функции и flow
👉 Пишем поддерживаем и читаемый код в тестах с Mockk
А вы мокаете данные в тестах?
Не все согласны, что это хороший подход, но существующий и практикующий многими компаниями:
👉 Mockk и основы
👉 Mockk, продвинутое использование. Мокаем статик методы, синглтоны и
final
классы👉 Unit-тестирование Android компонентов
👉 Мокаем suspend функции и flow
👉 Пишем поддерживаем и читаемый код в тестах с Mockk
А вы мокаете данные в тестах?
MockK
Provides DSL to mock behavior. Built from zero to fit Kotlin language. Supports named parameters, object mocks, coroutines and extension function mocking
Оптимизируем
Если кратко, то используем key:
Получаем:
👉 Улучшение перфоманса при рекомпозиции
👉 Нормальный контроль над состоянием элементов
Следите чтобы ключи были уникальными!
LazyList
в 1 строчку. Если кратко, то используем key:
LazyColumn {
items(items = yourList, key = { it.id }) { item ->
// Your item UI here
Text(text = item.name)
}
}
Получаем:
👉 Улучшение перфоманса при рекомпозиции
👉 Нормальный контроль над состоянием элементов
Следите чтобы ключи были уникальными!
Medium
Jetpack compose — One Line trick for your ‘LazyColumn’
Make sure you are using this in your Jetpack Compose ‘LazyColumn’
Эволюция архитектурных паттернов
👇 UI-центричная архитектура или
👇
👇
👇
👇
👉 Постепенное упрощение
Внутри статьи примеры с каждым этапом. Кому-то посмотреть как было раньше, кому-то понастольгировать
👇 UI-центричная архитектура или
God Activity
👇
MVC
. Выделили логику в контроллер👇
MVP
. Добавили интерфейсов меж слоями👇
MVVM
. Изоляция UI и реактивщина👇
MVI
и начало эры UDF
👉 Постепенное упрощение
MVI
с дроблением reduce
функции. TEA
, как хороший примерВнутри статьи примеры с каждым этапом. Кому-то посмотреть как было раньше, кому-то понастольгировать
Medium
The Evolution of Android Architecture Patterns: From UI-Centric to MVC to MVP to MVVM to MVI
A time journey through Android’s Architecture Patterns evolution which will leave you with a solid understanding over MVC, MVP, MVVM and…
Шпаргалка по размещению Composable элементов
Подробнее про
Про центрирование и растягивание тут
Подробнее про
Arrangement
с примерами можно глянуть тутПро центрирование и растягивание тут