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

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

Размещение рекламы: @tanyasanovna
Download Telegram
Вроде только-только Android 15 зарелизили, а уже надо закладывать в беклог поддержку фичей из Android 16
😱4👍3
Forwarded from Android Live 🤖
Что нового в Android 16?
#android #google

Что же, в этом году нас ждёт свежий Android, и к его нововведениям стоит подготовиться. Давайте разберём основные изменения, опираясь на официальную документацию Google.

🔴Progress-centric Notifications — фактически аналог Live Activities на iOS, где теперь у нас добавляется Notification.ProgressStyle для отображения прогресса текущего процесса. Полезно для приложений с загрузками, доставками и навигацией. Выглядит здорово, одобряем.

🔴Predictive back updates — новые API для предсказуемого поведения жестов "назад". Теперь onBackInvokedCallback позволяет точнее обрабатывать нажатия. Пока неясно, насколько это улучшит UX — навигация жестами и так работает достаточно стабильно.

🔴Richer Haptics — более классная обработка haptic-эффектов. В VibrationEffect.Compositions, добавили более точную настройку вибрационных эффектов. Одобряю, очень мало приложений хорошо используют haptic-эффект.

🔴System-triggered profiling — обновлённый ProfilingManager, появившийся в Android 15, теперь собирает ещё больше данных о производительности приложений. Не тестировал, если пробовали — расскажите, как вам?

🔴Better job introspection — полезное обновление, призванное улучшить дебаг запущенных Job. Теперь можно просматривать их историю и анализировать причины задержек или неудачного выполнения. Круто.

🔴Photo picker improvements — Google активно продвигает использование Photo Picker, а в новой версии API добавляет Embedded Photo Picker который делает выбор изображений ещё более естественным и встроенным в приложение. Отличное обновление.

🔴Vertical text — небольшая, но любопытная доработка: теперь Paint поддерживает вертикальный текст. Не знаю, можно ли было раньше сделать это без библиотек, но Google решил это подстветить как фичу. Подсвечу и я.

Тут только некоторые из фичей, хотя и основные, а все можно увидеть тут.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍91🥱1
Разбираем использование runBlocking в Android

👉 Что такое runBlocking?
Функция, которая запускает новую корутину и блокирует текущий поток до её завершения.
Часто используется в примерах для демонстрации корутин в main() функции.

👉 Почему стоит быть осторожным с runBlocking в Android?
Блокирует текущий поток, что на Android может привести к замораживанию UI и возникновению ANR (Application Not Responding).
Особенно критично при выполнении операций ввода-вывода в главном потоке.

👉 Пример неправильного использования:
class MainViewModel(private val mainRepository: MainRepository) : ViewModel() {
// Не рекомендуется
private fun fetchPosters() = runBlocking {
mainRepository.fetchPosters()
}
}

В этом примере runBlocking блокирует главный поток, что может привести к проблемам с отзывчивостью приложения.

👉 Альтернативы:
Используйте viewModelScope для запуска корутин, что обеспечивает автоматическую отмену задач при уничтожении ViewModel.
Применяйте launch или async для асинхронных операций без блокировки главного потока.
😁14👍7🔥3👎1💔1
This media is not supported in your browser
VIEW IN TELEGRAM
Небольшая свежая библиотека KAnalytics (⭐️ 15)

Не сказал бы, что на этой стадии я бы стал её использовать, но она может быть хорошим примером того, как стоит держать аналитику в приложении: с удобным дебаггером, поддержкой KMP и без глубокой связности с основным приложением.
👍2
Разбираем, что происходит при нажатии кнопки "Старт" в Android Studio

Или как собрать и запустить приложение без Android Studio:

👉 Генерируем APK-файл
./gradlew assembleDebug

👉Запуск эмулятора
emulator -list-avds
emulator -avd <имя_эмулятора>

👉 Установка APK
adb install -r app/build/outputs/apk/debug/app-debug.apk

👉 Запуск
adb shell am start -n <package_name>/<activity_name>

UPD: В комментариях уточнение по поводу сборки билда и выбора эмулятора от подписчика!
👍64😁1🤔1🤪1
Android Good Reads
Про Compose! 👉 Compose Multiplatform получила релиз 1.8.0-alpha02. Внутри в основном багфиксы и поддержка drag-and-drop для iOS 👉 Как запустить анимацию одновременно для 2 элементов? 👉 Можно ли двигать элементы по своему, когда открываем IME (клавиатуру)?…
Продолжаем про Compose!

👉 Можно ли сделать скролл чуть медленнее и плавнее? Да, с помощью NestedScrollConnection
👉 Почему ContextualFlowRow депрекейтнули? Начиная с Jetpack Compose 1.8 beta01 множество api выходит из статуса experimental, однако часть уезжает сразу в Deprecated

👉 Пачка зависимостей получила обновление:

androidx.lifecycle:lifecycle-runtime-compose:2.9.0-alpha09
androidx.lifecycle:lifecycle-runtime-compose-android:2.9.0-alpha09
androidx.lifecycle:lifecycle-runtime-compose-jvmstubs:2.9.0-alpha09
androidx.lifecycle:lifecycle-runtime-compose-linuxx64stubs:2.9.0-alpha09
androidx.lifecycle:lifecycle-viewmodel-compose:2.9.0-alpha09
androidx.lifecycle:lifecycle-viewmodel-compose-android:2.9.0-alpha09
androidx.lifecycle:lifecycle-viewmodel-compose-desktop:2.9.0-alpha09
androidx.navigation:navigation-compose:2.8.6
androidx.navigation:navigation-compose:2.9.0-alpha05
androidx.navigation:navigation-fragment-compose:2.8.6
androidx.navigation:navigation-fragment-compose:2.9.0-alpha05
androidx.savedstate:savedstate-compose:1.3.0-alpha07
androidx.savedstate:savedstate-compose-android:1.3.0-alpha07
androidx.savedstate:savedstate-compose-jvmstubs:1.3.0-alpha07
androidx.savedstate:savedstate-compose-linuxx64stubs:1.3.0-alpha07


👉 Полезный плагин: Figma to Jetpack Compose Icon сконвертирует иконку сразу в нужный формат в приложении
👍6🥱1
5 ошибок в корутинах о которых никто не говорит

Внутри разбор с примерами и объяснением почему это ошибка:
👉 Вызов suspend функции напрямую из вьюхи
👉 Некорректное использование GloablScope. В Android разработке его вообще некорректно использовать. Есть ли у вас примеры, когда это уместно?
👉 Последовательный, а не асинхронный запрос данных
👉 Вылов CancellationException внутри suspend функции
👉 В длительных цикличных операциях забываем про ensureActive()
👍12🔥4👏3
Частая проблема в проектах, где я работаю - синхронизация времени клиента и предотвращение перевода часов через настройки. А тут Google представляет TrustedTime API, который должен решить эту проблему наконец-таки.
Как использовать?

👉 Подключаем зависимость com.google.android.gms:play-services-time:16.0.1
👉 Создаем где-нибудь экземпляр

TrustedTime.createClient(context)

👉 Используем с фолбеком к стандартному способу

val currentTimeMillis =
myApp.trustedTimeClient?.computeCurrentUnixEpochMillis()
?: System.currentTimeMillis()


Под капотом синхронизация с Google серверами. Буду интегрировать в проект наряду с kotlinx-datetime
👍15🔥63
Android Good Reads
Продолжаем про Compose! 👉 Можно ли сделать скролл чуть медленнее и плавнее? Да, с помощью NestedScrollConnection 👉 Почему ContextualFlowRow депрекейтнули? Начиная с Jetpack Compose 1.8 beta01 множество api выходит из статуса experimental, однако часть уезжает…
Compose 👨‍💻

👉 Релиз 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
🔥6👍4👏3
В чатах по KMP часто всплывает вопрос: "Вот я Android разработчик, с чего мне начать изучать про KMP?" и, помимо официальной доки, ответить то было и нечего. Но вот появилась достаточно хорошо структурированная книга (open-source):
https://santimattius.github.io/kmp-for-mobile-native-developers-book/

Внутри примеры кода, разбор структуры и основных фреймворков. Пробовали ли KMP и что посоветуете начинающим?
👍15🤔5😁3🔥1
Упрощенное тестирование 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!
🔥16👍7
Новая typesafe навигация в Jetpack Compose привносит новые сложности. На деле, все сильно проще. О том как делать вложенную навигацию с BottomNavBar как раз следующая статья. Внутри:

👉 Примеры 2 разделенных графов навигации
👉 Пример основного экрана с BottomNavBar
👉 Судя по всему совместимо с CMP, код будет выглядеть 1 в 1

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

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

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