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

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

Размещение рекламы: @tanyasanovna
Download Telegram
Ультимативный гайд по написанию чисто кода на Jetpack Compose

Тут приведу основные идеи для @Composable функции, а то как надо и как не надо можно увидеть внутри статьи:

👉 Именование функций должно быть в PascalCase
👉 Последовательность параметров
Официальная рекомендация Android: Обязательные параметры -> Modifier -> необязательные параметры -> Вложенная @Composable
Более удобная версия: Modifier -> входные данные -> UI параметры -> калбеки -> Вложенная @Composable
👉 Либо возвращаем значение из @Composable, либо что-то рисуем. Но не одновременно
👉 У каждой функции не более 1 лайаута и при этом она должна быть независимой от места вызова
👉 Каждая функция должна содержать единственный Modifier со стандартным значением
👉 Старайтесь не держать собственное состояние внутри каждой @Composable функции
👉 Используйте отступы предоставляемые через Scaffold

Примеры как надо и как не надо читаем тут
👍9🥴3🔥2👌2
Android Good Reads
А вот и релиз! Кратко: 👉 Новые экспериментальные фичи. Guard условие в when, break и continue не только для своего скоупа. Разбирали частично ---> тут 👉 Обновили K2 компилятор и сделали ряд улучшенией в kapt 👉 Для KMM стабилизировали Gradle DSL, много улучшений…
И еще раз про Guard Conditions

👉 Как включить:
1) Проверяем что стоит галочка напротив Enable K2 mode в IDE
2) Добавляем 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}")
}
👍9🔥3
JetBrains исследует возможность добавить Hot Reload в Compose
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
🔥14👍2
MVI + Jetpack Compose что и как!

Внутри статьи немного теории о том как устроен MVI (Model - View - Intent) и как он работает.

👉 Hilt, KSP, Room и ViewModel как основа для проекта
👉 Compose навигация

Справедливо заметили, что при таком подходе, ViewModel должна отвечать только за управление состоянием, а разруливание корутин должно уйти на слой ниже, в репозиторий.

Финальный вид приложения можно посмотреть на GitHub
👏4👎3😁3👍2🤔1🤣1
Ищем блокирующие вызовы из неблокирующих потоков

Проверяем приложение на наличие блокирующих вызовов. Они могут вызывать микрофриз, а не краш приложения. С библиотекой 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
👍3🔥3
Начиная с версии 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
👍5🤷5🔥1
This media is not supported in your browser
VIEW IN TELEGRAM
Android XR SDK Developer Preview

Платформа для расширенной реальности теперь и на Android. Базируется так же, на open source - OpenXR. Все приложения на Android должны быть совместимы

Пощупать в эмуляторах можно тут, доступно для разработки начиная с Android Studio Meerkat
👏12🔥5👍2
О разработке SDK

Что нужно помнить при работе с SDK, как делать не нужно:

👉 Разработка SDK != разработка клиентского приложения. Например, не стоит перехватывать UI у клиентского приложения, если внутри SDK возникла ошибка. Лучше, прокиньте состояние ошибки и дайте пользователям SDK самим решать как ее отобразить.
👉 Предоставление пользовательского интерфейса через SDK влечет за собой ряд проблем: Отсутствие гибкости, конфликты ресурсов, увеличение размера SDK и сложности в его обслуживании, а так же проблемы масштабируемости. Но если вам все-таки необходимо поставлять UI с SDK, то разделите свое SDK на 2 разных package: UI и Core Logic с возможностью их раздельного использования. Не забудьте про Analytics Hooks в UI модуле!
👉 Минимизируйте внешние зависимости. А если используете - то лучше всего их изолировать. Неприятно ради 1 библиотеки подключать RxJava и Lottie, для конвертации в свой формат. Не забывайте, что не все библиотеки предоставляют лицензию на использование в коммерческих целях, например, GPL лицензии
❗️Старайтесь избегать глобальных синглтонов. Неприятно, когда в приложении появляется утечка памяти, а я никак не могу на это повлиять, кроме как отказаться от вашего SDK.
👉 Пишите документацию и CHANGELOG

С какими неприятными последствиями спорных решений в SDK вы сталкивались?
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7👍3💯2😐1
Анонс Koin IDE Plugin

Что позволит делать плагин:
👉 Видеть все дерево зависимостей
👉 Навигация между компонентами

Валидация на уровне Koin делается в рантайме, но через плагин можно проверить валидность состояния с помощью статического анализа. Предварительная дата релиза - 1 квартал 2025
🔥24👏3👍2
Разбираем Low Memory Management в Android: Kswapd & 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 внутри статьи или в этом видео
👍83🔥2🤔2😁1
Android Good Reads
Android XR SDK Developer Preview Платформа для расширенной реальности теперь и на Android. Базируется так же, на open source - OpenXR. Все приложения на Android должны быть совместимы Пощупать в эмуляторах можно тут, доступно для разработки начиная с Android…
Android XR KMP

Добавить поддержку 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 платформы сразу
🥴9👍2
Цикл статей про Mockk и тесты
Не все согласны, что это хороший подход, но существующий и практикующий многими компаниями:

👉 Mockk и основы
👉 Mockk, продвинутое использование. Мокаем статик методы, синглтоны и final классы
👉 Unit-тестирование Android компонентов
👉 Мокаем suspend функции и flow
👉 Пишем поддерживаем и читаемый код в тестах с Mockk

А вы мокаете данные в тестах?
👍4🔥2👎1👏1
Оптимизируем LazyList в 1 строчку.

Если кратко, то используем key:
LazyColumn {
items(items = yourList, key = { it.id }) { item ->
// Your item UI here
Text(text = item.name)
}
}


Получаем:
👉 Улучшение перфоманса при рекомпозиции
👉 Нормальный контроль над состоянием элементов

Следите чтобы ключи были уникальными!
👍6🤯3🔥2
Эволюция архитектурных паттернов

👇 UI-центричная архитектура или God Activity
👇 MVC. Выделили логику в контроллер
👇 MVP. Добавили интерфейсов меж слоями
👇 MVVM. Изоляция UI и реактивщина
👇 MVI и начало эры UDF
👉 Постепенное упрощение MVI с дроблением reduce функции. TEA, как хороший пример

Внутри статьи примеры с каждым этапом. Кому-то посмотреть как было раньше, кому-то понастольгировать
👍14🔥43👎2
Шпаргалка по размещению Composable элементов

Подробнее про Arrangement с примерами можно глянуть тут
Про центрирование и растягивание тут
🔥26👍85
Смотрим начинку ViewModel

👉 Основная логика лежит в ViewModelImpl (можно посмотреть тут)
👉 isCleared, Простой флаг указывающий на состояние viewModel. Много функций завязано на нем
👉 closeables, keyToCloseables, addCloseable(). Все призвано для управления AutoCloseable ресурсами (Что за AutoCloseable?)
👉 clear() Подчищает эти самые ресурсы

Как думаете, что мы чаще всего добавляем в addCloseable()? Правильно - viewModelScope, который в свою очередь является наследником AutoCloseable и CoroutineScope, которая останавливается по закрытию viewModel.


internal fun createViewModelScope(): CloseableCoroutineScope {
// dispatcher initialization
return CloseableCoroutineScope(coroutineContext = dispatcher + SupervisorJob())
}

internal class CloseableCoroutineScope(
override val coroutineContext: CoroutineContext,
) : AutoCloseable, CoroutineScope {

constructor(coroutineScope: CoroutineScope) : this(coroutineScope.coroutineContext)

override fun close() = coroutineContext.cancel()
}
👍10🔥4👏1
Разбираемся с CameraX в Jetpack Compose

Первая часть тут. В ней базовая настройка камеры и превью для камеры. Иными словами - Getting Started гайд из документации.
Что же подчерпнуть из этой части? Если вы не знаете как сделать tap-to-focus фичу

Полный пример фичи тут. А объяснение кода внутри статьи
👍3🔥2👏1