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

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

Размещение рекламы: @tanyasanovna
Download Telegram
Про стирание типов!

Если кратко - вся статья про 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()
}


Ответ:
test1() // Вывод: Start doSomething, Inside lambda
test2() // Вывод: Start doSomethingSafely, Inside lambda, End doSomethingSafely, This WILL be printed
👍84🔥3
Android Good Reads
Пятница! Если уж релизите в пятницу, то проведите дополнительные тесты, а именно Accessibility Check Android Studio Iguana уже стабильна, а это значит вам точно доступен UI check mode, которая покажет как на разных экранах будет выглядеть ваше приложение…
Ещё немного про Accessibility, но теперь и в Compose

👉 Конечно, про contentDescription, это важно даже для разных состояний кнопок
👉 onClickLabel который помогает вызову TalkBack функции
👉 minimumTouchTargetSize(), если не хотите думать над кастомной областью клика для иконок

Документация про Accessibility: https://developer.android.com/develop/ui/compose/accessibility
2🔥2
Разбираемся в разнице между try-catch и runCatching

👉 runCatching возвращает Result, что позволяет использовать экстеншены с Result и поддерживать функциональный стиль кода
👉 runCatching это скоуп функция, поэтому все возможности скоуп функций доступны
👉 Легко заменить try-catch с пустым catch блоком на runCatching
👉 Проще отлавливать вложенные exception


Зеркало с примерами. С runCatching код выглядит чище и в одном стиле.
👍4
Android Good Reads
Compose 👨‍💻 👉 Релиз androidx.media3:media3-ui-compose, UI для ExoPlayer через Compose будет рисовать проще 👉 Проблемы с изменением размера ModalBottomSheet ? 👉 Норм ли ранний дроп в @Composable? @Composable fun Label(val label: String?) { if(label ==…
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
👍2
Защищаемся от MITM атак

Немного про безопасноть можно почитать тут 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 с доверием к любым сертифкатам без лишних вопросов?
👍12🔥4👏31👎1
Используем UseCase как функцию из 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)
}
}


Удобно и понятно на что писать тесты! Используем?
🤷‍♂21👍4😢3🤓2
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
🔥15👍3
Ныне популярный vibe-coding теперь и в Android Studio благодаря Firebender!

По своей сути, то же самое, что и Cursor, Trae и windsurf, но встроено внутрь Android Studio. Демо для тех кто слышит об этом впервые можно глянуть тут: https://firebender.com/

Это кратно удобнее, чем задавать вопросы в чат. Доступны как популярные модели claude-3.7, так и локально запущенные llm. Пробовали ли на своих проектах работу с AI агентами в паре?
🔥11🗿9👍2🤣21👎1👨‍💻1
Пишем тесты для 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 }
}


Выглядит очень просто, в связи с чем вопрос: Тестирование навигации это тесты ради тестов или валидный способ проверки бизнес-логики?
2💯2👍1
Никогда не знаешь какой следующий фреймворк выстрелит, поэтому сегодня следим за Metro.

Легковесный 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
👍8🔥2👎1🤔1
Анализируем время сборки и рисуем графики

👉 Базовый Build Analyzer из Android studio покажет куда тратиться основная масса времени в сборке.
👉 Аналогичный репорт можно получить через ./gradlew --profile assemble[Flavor]Debug.
👉 Так же скан можно выгрузить в web и пошарить с коллегами через ./gradlew --scan. Там вы увидите более подробную разбивку по затраченному времени на тот или иной шаг сборки
👉 Gradle Profiler и благодаря его удобной функции - сценарии, можно получить наглядную статистику. Больше примеров тут
👍123
Ищем ненужные зависимости в Dagger/Hilt графе

В большом проекте - большой граф зависимостей компонентов, с лишними инжектами. Проблема решается с помощью Dagger SPI.
Автор предлагает с помощью SPI строить полное дерево зависимостей, а затем вытаскивать наружу неиспользуемые инжекты.

На малом проекте, проще это все проверять руками, но если у вас гигантский многомодульный проект, то прогнать разок весь проект с легаси модулями по инструкции из статьи будет полезно для приложения.
👍62🔥2
Дебажим корутины прямо в Android Studio

Дебажить асинхронщину это всегда проблема. Предлагается 3 способа:
👉 Классическое логирование println( “${msg} | currentThread: ${Thread.currentThread().name}”)
👉 VM option в конфигурации при запуске сборки: -Dkotlinx.coroutines.debug
👉 При помощи DebugProbes можно получить состояние корутины в ходе тестов. Более подробно внутри статьи (Option 4)
👍7🔥2
Очень тихо зарелизилось мощное улучшение для App Distribution - AI testing agent. Как по мне, это самое полезное для Android разработки из последних релизов в области AI. Кратко:
The App Testing agent is a test case generation, management, and execution agent powered by Gemini in Firebase. You define test goals in natural language, and the agent uses AI to understand and navigate your app, simulate user interactions, and provide detailed test results.

Прошли времена описания тестов на естественном языке (Gherkin + Cucumber). Выглядит все оч просто - вы задаете название, цель, подсказки и критерии приемки. Тесты запускаются в облаке на девайсах.
🔥17👍32
Как K2 mode помогает работать IDE быстрее

Режим K2 вводит новый анализатор кода, общий для компилятора Kotlin и IDE. Он заменяет implicit laziness поэтапным подходом к анализу кода, где каждый этап анализирует определённые части кода и добавляет семантическую информацию в абстрактное синтаксическое дерево (AST). Хотя ядро компилятора остаётся однопоточным, логика разрешения теперь устойчива к параллелизму, что позволяет одновременно анализировать несколько инициализаций
👍82🌚1
Разбираем порядок вызова DisposableEffect, LaunchedEffect и Composable

Если кратко - сначала вызывается композиция всех Composable и регистрируются блоки Effect, а затем вызываются сами эффекты. DisposableEffect, SideEffect, SnapshotFlow ... вызываются раньше чем LaunchedEffect, так как первые это синхронный вызов, запускающиеся сразу после композиции, а последний стартует корутину с помощью Recomposer.

Немного интереснее становится при навигации. Сначала будут вызваны эффекты на новом экране и только потом onDispose с экрана с которого ушли. Все это призвано убрать глитчи при переходе между экранами.
👍16🔥53🤔1
Разница между job.cancel() и scope.cancel()

👉 job.cancel() тормозит конкретную корутину
👉 scope.cancel() тормозит весь скоуп и все корутины в нем
👉 Можно остановить все корутины в скоупе, но не прибивать сам скоуп с помощью scope.coroutineContext.cancelChildren()
👉 viewModelScope и lifecycleScope останавливаются сами, не нужно этого делать вручную

Внутри статьи больше примеров и аналогий, если вы теряетесь в понимании что есть что
👍9
Разбираем context параметры в Kotlin 2.2.0

Анонс был, а прикладных примеров еще не было. Внутри примеры с тем как делать логирование и DI.
Из приятных плюсов:
- Убирает шаблонный код
- Повышение читабельности и поддерживаемости
- Типобезопасность

Пример комбинации с Extension функцией:
// Extension function with context parameter
context(txManager: TransactionManager)
fun List<Transaction>.processAllInTransaction() {
txManager.beginTransaction()
try {
forEach { it.process() }
txManager.commitTransaction()
} catch (e: Exception) {
txManager.rollbackTransaction()
throw e
}
}

// Usage
with(transactionManager) {
transactions.processAllInTransaction()
}
👍11🔥3👎2👌2
Хорошая новость этой недели! Compose Multiplatform вышел в релиз!
Из главного:
👉 Стабильность и перформанс, стабильность и перформанс!
👉 iOS полностью стабилен. Завезли немного нативного функционала (жесты, drag-n-drop, RTL поддержка ...)
👉 Множество апи вышло из стадии экспериментальных в стабильные!
👉 Улучшили холодный старт, перформанс приложения при скролле, а так же оптимизировали размер собираемого приложения
👉 Еще раз напомнили про hot-reload. Успели попробовать?

Очевидно, релиз под грядущий Kotlin Conf. Возможно, на самой конференеции будут еще анонсы, но уже сегодня я затаскиваю версию 1.8.0 в наше приложение
🔥15🫡4👍2