5 ошибок в корутинах о которых никто не говорит
Внутри разбор с примерами и объяснением почему это ошибка:
👉 Вызов
👉 Некорректное использование
👉 Последовательный, а не асинхронный запрос данных
👉 Вылов
👉 В длительных цикличных операциях забываем про
Внутри разбор с примерами и объяснением почему это ошибка:
👉 Вызов
suspend
функции напрямую из вьюхи👉 Некорректное использование
GloablScope
. В Android разработке его вообще некорректно использовать. Есть ли у вас примеры, когда это уместно?👉 Последовательный, а не асинхронный запрос данных
👉 Вылов
CancellationException
внутри suspend
функции👉 В длительных цикличных операциях забываем про
ensureActive()
Частая проблема в проектах, где я работаю - синхронизация времени клиента и предотвращение перевода часов через настройки. А тут Google представляет TrustedTime API, который должен решить эту проблему наконец-таки.
Как использовать?
👉 Подключаем зависимость
👉 Создаем где-нибудь экземпляр
👉 Используем с фолбеком к стандартному способу
Под капотом синхронизация с Google серверами. Буду интегрировать в проект наряду с kotlinx-datetime
Как использовать?
👉 Подключаем зависимость
com.google.android.gms:play-services-time:16.0.1
👉 Создаем где-нибудь экземпляр
TrustedTime.createClient(context)
👉 Используем с фолбеком к стандартному способу
val currentTimeMillis =
myApp.trustedTimeClient?.computeCurrentUnixEpochMillis()
?: System.currentTimeMillis()
Под капотом синхронизация с Google серверами. Буду интегрировать в проект наряду с kotlinx-datetime
Android Developers Blog
TrustedTime API: Introducing a reliable approach to time keeping for your apps
The Trusted Time API in Google Play Services offers developers a reliable, accurate time source independent of device settings to ensure consistency.
Android Good Reads
Продолжаем про Compose! 👉 Можно ли сделать скролл чуть медленнее и плавнее? Да, с помощью NestedScrollConnection 👉 Почему ContextualFlowRow депрекейтнули? Начиная с Jetpack Compose 1.8 beta01 множество api выходит из статуса experimental, однако часть уезжает…
Compose 👨💻
👉 Релиз
👉 Проблемы с изменением размера
👉 Норм ли ранний дроп в
или
👉 Обновилась библиотека для Compose-driven architecture - Circut
👉 Coil получил обновление 3.1.0 c улучшением перфоманса для
👉 Релиз
androidx.media3:media3-ui-compose
, UI для ExoPlayer через Compose будет рисовать проще👉 Проблемы с изменением размера
ModalBottomSheet
?👉 Норм ли ранний дроп в
@Composable
?
@Composable
fun Label(val label: String?) {
if(label == null) return
...
}
или
@Composable
fun Label(val label: String?) {
if(label != null){
...
}
}
👉 Обновилась библиотека для Compose-driven architecture - Circut
👉 Coil получил обновление 3.1.0 c улучшением перфоманса для
AsyncImage
Please open Telegram to view this post
VIEW IN TELEGRAM
В чатах по KMP часто всплывает вопрос: "Вот я Android разработчик, с чего мне начать изучать про KMP?" и, помимо официальной доки, ответить то было и нечего. Но вот появилась достаточно хорошо структурированная книга (open-source):
https://santimattius.github.io/kmp-for-mobile-native-developers-book/
Внутри примеры кода, разбор структуры и основных фреймворков. Пробовали ли KMP и что посоветуете начинающим?
https://santimattius.github.io/kmp-for-mobile-native-developers-book/
Внутри примеры кода, разбор структуры и основных фреймворков. Пробовали ли KMP и что посоветуете начинающим?
Упрощенное тестирование ViewModel
Начиная с
Совместимо с KMP!
Начиная с
androidx.lifecycle:lifecycle-viewmodel-testing:2.9.0-alpha01
нам доступна структура ViewModelScenario
для проверки того как ViewModel
переживает реконфигурацию или восстановление состояния. Пример:
class MyViewModelTest {
@Test
fun testStateRestoration() = runTest { // this: TestScope
viewModelScenario { // this: CreationExtras
MyViewModel(
scope = this@runTest,
handle = createSavedStateHandle(),
)
}.use { scenario: ViewModelScenario ->
// Устанавливаем `ViewModel` state.
scenario.recreate()
// Проверяем что state восстановился корректно
}
}
}
Совместимо с KMP!
Новая typesafe навигация в Jetpack Compose привносит новые сложности. На деле, все сильно проще. О том как делать вложенную навигацию с BottomNavBar как раз следующая статья. Внутри:
👉 Примеры 2 разделенных графов навигации
👉 Пример основного экрана с
👉 Судя по всему совместимо с CMP, код будет выглядеть 1 в 1
Не стоит забывать и о документации
👉 Примеры 2 разделенных графов навигации
👉 Пример основного экрана с
BottomNavBar
👉 Судя по всему совместимо с CMP, код будет выглядеть 1 в 1
Не стоит забывать и о документации
Rock and Null
Flat approach for tabbed Navigation in Jetpack Compose
Simplify Jetpack Compose tabbed navigation with a flat structure and conditional visibility, avoiding complex nested graphs. This approach solves URL mapping issues in Compose Web and streamlines deep linking.
Опасности добавления permission READ_MEDIA_IMAGES
Если вы релизите часто приложение, вы верно знаете о правилах гугла в отношении
Если вы релизите часто приложение, вы верно знаете о правилах гугла в отношении
permission
. Автор статьи решает задачу отображения превью существующих фото из девайса. О его попытках можно узнать тут, но как итог - фичу полностью ревертнули, чтобы не блокировать релиз.Про стирание типов!
Если кратко - вся статья про
Ответ:
test1() // Вывод: Start doSomething, Inside lambda
test2() // Вывод: Start doSomethingSafely, Inside lambda, End doSomethingSafely, This WILL be printed
Если кратко - вся статья про
inline reified
. Внутри так же примеры для noinline
и crossinline
. Заметил хороший практический пример про работу с crossinline
, который можно было бы спросить на собеседовании на понимание:inline fun doSomething(action: () -> Unit) {
println("Start doSomething")
action()
println("End doSomething") // This might NOT be printed
}
fun test1() {
doSomething {
println("Inside lambda")
return // This exits test1(), NOT just the lambda!
}
println("This will NOT be printed")
}
inline fun doSomethingSafely(crossinline action: () -> Unit) {
println("Start doSomethingSafely")
action()
println("End doSomethingSafely") // This WILL be printed
}
fun test2() {
doSomethingSafely {
println("Inside lambda")
return // This exits ONLY the lambda
}
println("This WILL be printed")
}
fun main() {
println("Running test1:")
test1()
println("\nRunning test2:")
test2()
}
Ответ:
Android Good Reads
Пятница! Если уж релизите в пятницу, то проведите дополнительные тесты, а именно Accessibility Check Android Studio Iguana уже стабильна, а это значит вам точно доступен UI check mode, которая покажет как на разных экранах будет выглядеть ваше приложение…
Ещё немного про Accessibility, но теперь и в Compose
👉 Конечно, про
👉
👉
Документация про Accessibility: https://developer.android.com/develop/ui/compose/accessibility
👉 Конечно, про
contentDescription
, это важно даже для разных состояний кнопок👉
onClickLabel
который помогает вызову TalkBack функции👉
minimumTouchTargetSize
(), если не хотите думать над кастомной областью клика для иконок Документация про Accessibility: https://developer.android.com/develop/ui/compose/accessibility
Android Developers
Accessibility in Jetpack Compose | Android Developers
Разбираемся в разнице между try-catch и runCatching
👉
👉
👉 Легко заменить
👉 Проще отлавливать вложенные
Зеркало с примерами. С
👉
runCatching
возвращает Result, что позволяет использовать экстеншены с Result и поддерживать функциональный стиль кода👉
runCatching
это скоуп функция, поэтому все возможности скоуп функций доступны👉 Легко заменить
try-catch
с пустым catch
блоком на runCatching
👉 Проще отлавливать вложенные
exception
Зеркало с примерами. С
runCatching
код выглядит чище и в одном стиле.Medium
Kotlin Tips and Tricks You May Not Know: #7 — Goodbye try-catch, Hello runCatching!
A cleaner way to handle exceptions in Kotlin with runCatching.
Android Good Reads
Compose 👨💻 👉 Релиз androidx.media3:media3-ui-compose, UI для ExoPlayer через Compose будет рисовать проще 👉 Проблемы с изменением размера ModalBottomSheet ? 👉 Норм ли ранний дроп в @Composable? @Composable fun Label(val label: String?) { if(label ==…
Compose 😎
👉 Обновления: BOM ->
Минорные библиотеки:
👉 Зачем нужно прокидывать Initial State?
👉 Можно ли сделать горизонтально-прокручиваемый
В связи с этим, небольшой опрос о состоянии Compose в вашем проекте
👉 Обновления: BOM ->
2025.03.00
, Material3 теперь 1.4.0-alpha10
с кучей обновлений API, а Compose получил первый RC для версии 1.8.0. Ждем?Минорные библиотеки:
androidx.activity:activity-compose:1.11.0-alpha01
androidx.lifecycle:lifecycle-runtime-compose:2.9.0-alpha12
androidx.lifecycle:lifecycle-runtime-compose-android:2.9.0-alpha12
androidx.lifecycle:lifecycle-runtime-compose-jvmstubs:2.9.0-alpha12
androidx.lifecycle:lifecycle-runtime-compose-linuxx64stubs:2.9.0-alpha12
androidx.lifecycle:lifecycle-viewmodel-compose:2.9.0-alpha12
androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.0-alpha12
androidx.lifecycle:lifecycle-viewmodel-compose-desktop:2.9.0-alpha12
androidx.media3:media3-ui-compose:1.6.0-rc01
androidx.navigation:navigation-compose:2.8.9
androidx.navigation:navigation-compose:2.9.0-alpha08
androidx.navigation:navigation-compose-android:2.9.0-alpha08
androidx.navigation:navigation-compose-jvmstubs:2.9.0-alpha08
androidx.navigation:navigation-compose-linuxx64stubs:2.9.0-alpha08
androidx.navigation:navigation-fragment-compose:2.8.9
androidx.navigation:navigation-fragment-compose:2.9.0-alpha08
androidx.savedstate:savedstate-compose:1.3.0-alpha10
androidx.savedstate:savedstate-compose-android:1.3.0-alpha10
androidx.savedstate:savedstate-compose-jvmstubs:1.3.0-alpha10
androidx.savedstate:savedstate-compose-linuxx64stubs:1.3.0-alpha10
👉 Зачем нужно прокидывать Initial State?
👉 Можно ли сделать горизонтально-прокручиваемый
FlowRow
?В связи с этим, небольшой опрос о состоянии Compose в вашем проекте
Please open Telegram to view this post
VIEW IN TELEGRAM
Compose за прошедший год
Final Results
14%
Недавно начали миграцию на Compose
29%
Давно начали миграцию на Compose, еще в процессе
35%
Уже все на compose
22%
Мы остаемся верны XML
Защищаемся от MITM атак
Немного про безопасноть можно почитать тут OWASP Mobile 2024, а что можно предпринять, во избежании проблем:
👉 Использования
👉 Certificate Pinning, для предотвращения подмены сертификата. Тут, возможно, надо чуть больше подумать над реализацией, чтобы приложение не сломалось при изменениях сертификата.
👉 Использование Strong Network Security Configuration, которое, в целом, дает все преимущества первых двух пунктов, но работает начиная с Android 7.0
👉 Корректная валидация SSL/TLS сертификатов и отключение поддержки устаревших протоколов.
Есть ли у вас в проекте
Немного про безопасноть можно почитать тут OWASP Mobile 2024, а что можно предпринять, во избежании проблем:
👉 Использования
HTTPS
с TLS 1.2
, 1.3
. Валидные SSL/TLS
сертификаты. Использовать HSTS
(HTTP Strict Transport Security), который не позволяет свалиться обратно к HTTP
<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">yourdomain.com</domain>
</domain-config>
</network-security-config>
👉 Certificate Pinning, для предотвращения подмены сертификата. Тут, возможно, надо чуть больше подумать над реализацией, чтобы приложение не сломалось при изменениях сертификата.
val client = OkHttpClient.Builder()
.certificatePinner(
CertificatePinner.Builder()
.add("yourdomain.com", "sha256/your-certificate-hash")
.build()
)
.build()
👉 Использование Strong Network Security Configuration, которое, в целом, дает все преимущества первых двух пунктов, но работает начиная с Android 7.0
<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">yourdomain.com</domain>
<pin-set>
<pin digest="SHA-256">your-certificate-hash</pin>
</pin-set>
</domain-config>
</network-security-config>
👉 Корректная валидация SSL/TLS сертификатов и отключение поддержки устаревших протоколов.
val sslSocketFactory = SSLContext.getInstance("TLSv1.3").apply { init(null, null, null) }.socketFactory
val client = OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).trustManagers[0] as X509TrustManager)
.build()
Есть ли у вас в проекте
TrustManager
с доверием к любым сертифкатам без лишних вопросов?Используем UseCase как функцию из DI
Идея в том чтобы описать функцию, использующую DI для обработки входных данных. В итоге получим тестируемую функцию, которую можно переиспользовать
Пример:
Ну а внутри
Удобно и понятно на что писать тесты! Используем?
Идея в том чтобы описать функцию, использующую DI для обработки входных данных. В итоге получим тестируемую функцию, которую можно переиспользовать
Пример:
public class GoodReadsUseCase @Inject public constructor(
private val repository: Repository,
private val manager: Manager,
private val clock: Clock
) {
public suspend operator fun invoke(params: Params): Boolean {
return someServerLogic
}
}
Ну а внутри
ViewModel
это будет выглядеть как то так:
public class ViewModel @Inject constructor(
private val useCase: GoodReadsUseCase
) {
public suspend fun read(params: Params) {
val isBool = useCase(params)
}
}
Удобно и понятно на что писать тесты! Используем?
Устроиться мобильным разработчиком в Яндекс за выходные
12–13 апреля проводим Weekend Offer Mobile . До 9 апреля оставьте заявку на участие, 12 апреля пройдите технические собеседования, а 13 апреля познакомьтесь с командами и получите офер.
В мероприятии участвует 7 команд: Алиса и Умные устройства, Карты и Навигатор, Авто.ру, Недвижимость, Путешествия, Аренда, Рекламные технологии. Вы сможете пообщаться с менеджерами и выбрать проект, который покажется самым интересным.
Узнать подробности и зарегистрироваться можно здесь.
12–13 апреля проводим Weekend Offer Mobile . До 9 апреля оставьте заявку на участие, 12 апреля пройдите технические собеседования, а 13 апреля познакомьтесь с командами и получите офер.
В мероприятии участвует 7 команд: Алиса и Умные устройства, Карты и Навигатор, Авто.ру, Недвижимость, Путешествия, Аренда, Рекламные технологии. Вы сможете пообщаться с менеджерами и выбрать проект, который покажется самым интересным.
Узнать подробности и зарегистрироваться можно здесь.
Android Good Reads
JetBrains исследует возможность добавить Hot Reload в Compose https://github.com/JetBrains/compose-hot-reload (404 ⭐️ ) Мультиплатформа тоже поддерживается, судя по всему. Для сборки используеть отдельная версия официальных плагинов (2.1.0-firework.31) и сам…
HotReload теперь доступен тем, кто работает с Compose
Если вы скучаете по заброшенному и сломанному instant run, или немного завидовали друзьям с Flutter и React Native, которые обновляют UI во время разработки моментально - то теперь и вы так же можете делать с Compose!
Кстати! Недавний опрос показал, что большинство уже успело поработать с Compose, а значит эта фича приятно дополнит ваш рабочий процесс.
HotReload живет в отдельном репозитории: https://github.com/JetBrains/compose-hot-reload
Если вы скучаете по заброшенному и сломанному instant run, или немного завидовали друзьям с Flutter и React Native, которые обновляют UI во время разработки моментально - то теперь и вы так же можете делать с Compose!
Кстати! Недавний опрос показал, что большинство уже успело поработать с Compose, а значит эта фича приятно дополнит ваш рабочий процесс.
HotReload живет в отдельном репозитории: https://github.com/JetBrains/compose-hot-reload
Ныне популярный vibe-coding теперь и в Android Studio благодаря Firebender!
По своей сути, то же самое, что и Cursor, Trae и windsurf, но встроено внутрь Android Studio. Демо для тех кто слышит об этом впервые можно глянуть тут: https://firebender.com/
Это кратно удобнее, чем задавать вопросы в чат. Доступны как популярные модели
По своей сути, то же самое, что и Cursor, Trae и windsurf, но встроено внутрь Android Studio. Демо для тех кто слышит об этом впервые можно глянуть тут: https://firebender.com/
Это кратно удобнее, чем задавать вопросы в чат. Доступны как популярные модели
claude-3.7
, так и локально запущенные llm
. Пробовали ли на своих проектах работу с AI агентами в паре?Пишем тесты для compose навигации
Все благодаря
Выглядит очень просто, в связи с чем вопрос: Тестирование навигации это тесты ради тестов или валидный способ проверки бизнес-логики?
Все благодаря
type-safe
навигации, представленной недавно. Внутри статьи вариант для старого подхода и способ миграции его на новый подход. Для тестов используется TestNavHostController
, позволяющий верифицировать экран на котором находимся. Пример:
@Test
fun whenUserIsInBookSearch_andClickOnABook_verifyDestinationIsBookDetail() {
// User is in BookSearch
composeTestRule.runOnUiThread {
navController.setCurrentDestination(NavigationRoutes.BOOK_SEARCH)
}
// User clicks on the first book
composeTestRule.onAllNodesWithTag(bookCardTestTag)[0].performClick()
assertTrue { navController.currentDestination?.route == NavigationRoutes.BOOK_DETAIL }
}
Выглядит очень просто, в связи с чем вопрос: Тестирование навигации это тесты ради тестов или валидный способ проверки бизнес-логики?
Никогда не знаешь какой следующий фреймворк выстрелит, поэтому сегодня следим за Metro.
Легковесный compile-time DI без излишеств. Все фичи с примерами тут. Из приятного - поддержка KMP из коробки.
Основная репа: https://github.com/zacsweers/metro (260⭐️ )
Документация: https://zacsweers.github.io/metro/?ref=zacsweers.dev
Легковесный compile-time DI без излишеств. Все фичи с примерами тут. Из приятного - поддержка KMP из коробки.
Основная репа: https://github.com/zacsweers/metro (260
Документация: https://zacsweers.github.io/metro/?ref=zacsweers.dev
Please open Telegram to view this post
VIEW IN TELEGRAM
Zac Sweers
Introducing Metro
I'm excited to share something new I've been working on the past few months!
Анализируем время сборки и рисуем графики
👉 Базовый Build Analyzer из Android studio покажет куда тратиться основная масса времени в сборке.
👉 Аналогичный репорт можно получить через
👉 Так же скан можно выгрузить в web и пошарить с коллегами через
👉 Gradle Profiler и благодаря его удобной функции - сценарии, можно получить наглядную статистику. Больше примеров тут
👉 Базовый Build Analyzer из Android studio покажет куда тратиться основная масса времени в сборке.
👉 Аналогичный репорт можно получить через
./gradlew --profile assemble[Flavor]Debug
. 👉 Так же скан можно выгрузить в web и пошарить с коллегами через
./gradlew --scan
. Там вы увидите более подробную разбивку по затраченному времени на тот или иной шаг сборки 👉 Gradle Profiler и благодаря его удобной функции - сценарии, можно получить наглядную статистику. Больше примеров тут