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

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

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

Начиная с 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 разделенных графов навигации
👉 Пример основного экрана с BottomNavBar
👉 Судя по всему совместимо с CMP, код будет выглядеть 1 в 1

Не стоит забывать и о документации
Опасности добавления permission READ_MEDIA_IMAGES

Если вы релизите часто приложение, вы верно знаете о правилах гугла в отношении permission. Автор статьи решает задачу отображения превью существующих фото из девайса. О его попытках можно узнать тут, но как итог - фичу полностью ревертнули, чтобы не блокировать релиз.
Про стирание типов!

Если кратко - вся статья про 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
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
🚀 Оптимизируйте разработку — запускайте ресурсоемкие приложения в облаках Cloud․ru с GPU

Откройте для себя молниеносный рендеринг и топовую производительность. Гибкое масштабирование, высокая надежность и круглосуточная поддержка позволяют эффективно воплощать самые смелые идеи, не беспокоясь о технических ограничениях.

❄️ А главное: провайдер замораживает цены для новых клиентов на три года. Вы точно знаете, сколько тратите, и можете планировать IT-бюджет без неожиданных расходов. Спецпредложение действует для юридических лиц и ИП.

👉 Оставьте заявку до 31 марта 2025 года
Разбираемся в разнице между try-catch и runCatching

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


Зеркало с примерами. С runCatching код выглядит чище и в одном стиле.
Какая навигация в Compose лучшая? Может ли AI писать UI за вас? Как проходит переезд на Compose в большой компании?

С 24 по 28 марта на Podlodka Android Crew разберем всё, что накопилось за 3 года продакшн-использования Compose!

🔥 Дебаты по навигации в Compose — два эксперта - Аркадий Иванов (X (ex-Twitter) и Игорь Кареньков (HH.ru), два подхода, одна истина. Кто окажется убедительнее, решат зрители!

🤖 Как AI пишет UI за вас — Павел Королев и Алексей Утепов (QazCode) на практике покажут генерацию верстки реального макета в Jetpack Compose.

🏗 Опыт Avito: переезд на Compose — изменения затрагивают дизайн-систему, архитектуру, перфоманс, процессы. Ребята из Авито делятся опытом и советами.

🔍 Публичное собеседование по Compose — реальный кандидат, реальный код, разбор ошибок и живой фидбек.

Главное — всё, что обсудим, можно сразу применять на практике! Билеты тут 👉 https://podlodka.io/droidcrew
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
Защищаемся от 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 с доверием к любым сертифкатам без лишних вопросов?
Используем 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)
}
}


Удобно и понятно на что писать тесты! Используем?
Устроиться мобильным разработчиком в Яндекс за выходные

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
Ныне популярный vibe-coding теперь и в Android Studio благодаря Firebender!

По своей сути, то же самое, что и 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
Please open Telegram to view this post
VIEW IN TELEGRAM
Анализируем время сборки и рисуем графики

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

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

👉 Примите участие в опросе (ссылка) — это займет всего 5 минут, зато даст команде объективную обратную связь.

Исследование анонимно и конфиденциально, а все данные будут использованы для анализа только в обобщенном виде.
Ищем ненужные зависимости в Dagger/Hilt графе

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

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