Квиз!
В
Пример кода:
Ответ: клик
В
Composable
, в большинстве случаев, если мы используем mutableStateOf()
, мы фиксируем его состояние через remember()
, чтобы сохранить его при рекомпозициях. Но если мы используем mutableStateOf()
в Activity
— нужно ли нам использовать remember()
? Пример кода:
class MainActivity : ComponentActivity() {
private var text = mutableStateOf("Hello!")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppTheme {
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
TextField(value = text.value, onValueChange = {
text.value = it
})
Text(text = text.value)
}
}
}
}
}
Ответ: клик
Крутая находка: ksp форк anvil, фреймворка для помощи в инъекции зависимостей с Dagger 2
Зачем это было сделано?
Оригинальный фреймворк до сих пор на Kotlin 1.*, только на kapt и содержит проблемы с инкрементальной компиляцией. Форк умеет работать и с kapt и с ksp одновременно, а автор планирует продолжать его поддержку
KSP fork: https://github.com/zacsweers/anvil
Оригинал, для тех кто не знаком (1300+⭐️ ) : https://github.com/square/anvil
Зачем это было сделано?
Оригинальный фреймворк до сих пор на Kotlin 1.*, только на kapt и содержит проблемы с инкрементальной компиляцией. Форк умеет работать и с kapt и с ksp одновременно, а автор планирует продолжать его поддержку
KSP fork: https://github.com/zacsweers/anvil
Оригинал, для тех кто не знаком (1300+
Please open Telegram to view this post
VIEW IN TELEGRAM
Замените UseCase на Интерфейсы
Нашел невероятно красивое решение, как слой usecase, являющихся прокси к репозиторию, облегчить.
Допустим у нас есть репозиторий:
Тогда примитивные прокси usecase(представим их сразу как интерфейсы) будут выглядеть так:
Значит, в целом, мы можем представить репозиторий, как реализацию этих интерфейсов:
А раз есть реализация, значит можно инжектить ее напрямую в VM
Пример целиком, но с большим количеством обслуживающего кода: Гист
Вроде бы использованы базовые паттерны, но такой реализации еще не видел
Нашел невероятно красивое решение, как слой usecase, являющихся прокси к репозиторию, облегчить.
Допустим у нас есть репозиторий:
class CounterRepository {
private val _value = StateFlow(0)
val value: Flow<Int> = _value
fun addOne() { _value.update { it + 1 } }
fun subOne() { _value.update { it - 1 } }
}
Тогда примитивные прокси usecase(представим их сразу как интерфейсы) будут выглядеть так:
interface AddUseCase { fun addOne() }
interface SubUseCase { fun subOne() }
interface GetValueUseCase {
val value: Flow<Int>
}
Значит, в целом, мы можем представить репозиторий, как реализацию этих интерфейсов:
class CounterRepository:
AddUseCase,
SubUseCase,
GetValueUseCase {
override val value: Flow<Int> ...
override fun addOne() ...
override fun subOne() ...
}
А раз есть реализация, значит можно инжектить ее напрямую в VM
class CountingViewModel(
private val addOne: AddOneUseCase,
private val subOne: SubUseCase,
private val getValue: GetValueUseCase,
): ViewModel() {
...
}
Пример целиком, но с большим количеством обслуживающего кода: Гист
Вроде бы использованы базовые паттерны, но такой реализации еще не видел
Android Good Reads
Замените UseCase на Интерфейсы Нашел невероятно красивое решение, как слой usecase, являющихся прокси к репозиторию, облегчить. Допустим у нас есть репозиторий: class CounterRepository { private val _value = StateFlow(0) val value: Flow<Int> = _value…
Немного контекста к посту выше.
Подразумевалось избавление от сценариев, когда у вас UseCase выглядит вот так:
Убирая этот UseCase вам придется обращаться к репозиторию напрямую из ViewModel, что позволит вызывать и другие функции репозитория напрямую
Крутая дискуссия случилась в комментариях с разными точками зрения, спасибо!
Подразумевалось избавление от сценариев, когда у вас UseCase выглядит вот так:
class GetTaskListUseCase(
private val taskRepository: TaskRepository,
) {
operator fun invoke(): List<TaskItem> {
return taskRepository.getTaskItems()
}
}
Убирая этот UseCase вам придется обращаться к репозиторию напрямую из ViewModel, что позволит вызывать и другие функции репозитория напрямую
Крутая дискуссия случилась в комментариях с разными точками зрения, спасибо!
Со сложных архитектурных задач прошлой недели обратимся к популярным ошибкам в Jetpack Compose:
👉 Забываете использовать
👉 Постоянно используете
👉 Игнорируете оптимизации производительности. Делаете тяжелые операции при каждой рекомпозиции
👉 Путаете
👉 Не знаете как устроены
👉 Создаете сложные и неподдерживаемые
👉 Не используете тему и и цвета из нее, а применяете значения напрямую
👉 Забываете тестировать
👉 Игнорируете
Внутри статьи есть примеры ошибок и правильной версии по мнению автора.
👉 Забываете использовать
remember
, или используете его когда не надо, как тут👉 Постоянно используете
Modifier.fillMaxSize()
без надобности👉 Игнорируете оптимизации производительности. Делаете тяжелые операции при каждой рекомпозиции
👉 Путаете
State
и MutableState
. Почитать отличия можно тут👉 Не знаете как устроены
LaunchedEffect
и DisposableEffect
. Почитать тут, а в самой статье неплохие примеры про неверный key
, лишние рекомпозиции и ошибки жизненного цикла👉 Создаете сложные и неподдерживаемые
Composable
с глубокой вложенностью👉 Не используете тему и и цвета из нее, а применяете значения напрямую
👉 Забываете тестировать
Composable
. Для этого, кстати, не обязательно проходиться тест кейсами по пути до элемента, достаточно вызвать его отдельно и потыкать. Способы найти элемент внутри Composable
описаны тут👉 Игнорируете
contentDescription
. Доступность вашего приложения для людей с ограниченными возможностями очень важна! Не пренебрегайте этим, пожалуйста. О том как проверить свое приложение был пост тутВнутри статьи есть примеры ошибок и правильной версии по мнению автора.
Если вы теряетесь в ответе на вопрос "Почему нужно использовать тут
👉 Как работает data class под капотом
👉 Какие есть ограничения
👉 Какой синтаксический сахар предлагает
Бонус
Посмотреть на то как выглядит голый код вашего датакласса, возможно так понятнее, зайдя в
class
, а не data class
", то эта статья для вас. Конечно, документацию тоже нужно глянуть👉 Как работает data class под капотом
👉 Какие есть ограничения
👉 Какой синтаксический сахар предлагает
Бонус
Посмотреть на то как выглядит голый код вашего датакласса, возможно так понятнее, зайдя в
Tools
-> Kotlin
-> Show Kotlin Bytecode
и кликнуть Decompile
Ну штош...
Варианты организации мультимодульной архитектуры
1️⃣ Каждая фиче-директория содержит 2 сабмодуля:
Это сильно усложняет проект, но дает масштабируемость на больших проектах
2️⃣ Один общий модуль
Сильно упрощает проект, но если это небольшое приложение, то поможет в выделении всей бизнес-логики в отдельный модуль. Я, честно говоря, не вижу никаких преимуществ в таком подходе
3️⃣ Превращаем фиче-деректорию из п.1 в фиче-модуль с двумя сабмодулями
Бенефиты в виде дополнительного модуля на каждую фичу могут повлиять на скорость сборки, но дадут удобство в способе подключения модулей
4️⃣ Отдельный модуль для каждой фичи, и все это лежит в папке
Наверно это самый базовый способ вкатиться проекту в мультимодульную архитектуру, физически разделяя компоненты в приложении
Как и, главное, почему именно так, вы организовали разделение на модули в своем приложении?
Варианты организации мультимодульной архитектуры
1️⃣ Каждая фиче-директория содержит 2 сабмодуля:
domain
и presentation
, разделяя эти слои физически. Это сильно усложняет проект, но дает масштабируемость на больших проектах
2️⃣ Один общий модуль
features
для всех фичей приложения. Сильно упрощает проект, но если это небольшое приложение, то поможет в выделении всей бизнес-логики в отдельный модуль. Я, честно говоря, не вижу никаких преимуществ в таком подходе
3️⃣ Превращаем фиче-деректорию из п.1 в фиче-модуль с двумя сабмодулями
domain
и presentation
. Бенефиты в виде дополнительного модуля на каждую фичу могут повлиять на скорость сборки, но дадут удобство в способе подключения модулей
4️⃣ Отдельный модуль для каждой фичи, и все это лежит в папке
features
Наверно это самый базовый способ вкатиться проекту в мультимодульную архитектуру, физически разделяя компоненты в приложении
Как и, главное, почему именно так, вы организовали разделение на модули в своем приложении?
Medium
Approaches for Multi-Module Feature Architecture on Android
Designing an effective architecture for your Android project is crucial, especially when you aim to maintain it long-term. The strategy you…
Интересный сравнительный анализ кроссплатформы и нативной разработки у Никиты Куликова(@localhost_ru) . Понятно откуда берется использование излишних ресурсов, но для некоторых продуктов это реальный повод задуматься над резонностью использования Flutter/CMP
Telegram
Локалхост (Никита Куликов)
Почему кроссплатформа это плохое решение?
1) На скринах выше - одно и то же приложение, написанное нативно и с использованием кроссплатформы. "Экономия" на разработчиках приводит к ухудшению пользовательского опыта
2) Кроссплатформенное приложение писать…
1) На скринах выше - одно и то же приложение, написанное нативно и с использованием кроссплатформы. "Экономия" на разработчиках приводит к ухудшению пользовательского опыта
2) Кроссплатформенное приложение писать…
Обновляем приложение через Github Releases без маркета
Все достаточно очевидно:
👉 Выкладываем приложение на GitHub как последний релиз
👉 Через api GitHub'а тянем последнюю версию и информацию о ней
👉 Вытягиваем из артефактов релиза apk
👉 Запрашиваем установку пакета у пользователя
Для публичных релизов необходимо, чтобы ваш проект был опенсорсным + весь флоу немного портит пользовательский опыт. Откат от app bundle к apk файлу тоже выглядит грустно
Из плюсов, если вы используете гитхаб, то этот способ отлично встроится в ваш релизный процесс и моментально раскатит приложение на ваших пользователей, избегая длительного процесса код-ревью.
Все достаточно очевидно:
👉 Выкладываем приложение на GitHub как последний релиз
👉 Через api GitHub'а тянем последнюю версию и информацию о ней
👉 Вытягиваем из артефактов релиза apk
👉 Запрашиваем установку пакета у пользователя
Для публичных релизов необходимо, чтобы ваш проект был опенсорсным + весь флоу немного портит пользовательский опыт. Откат от app bundle к apk файлу тоже выглядит грустно
Из плюсов, если вы используете гитхаб, то этот способ отлично встроится в ваш релизный процесс и моментально раскатит приложение на ваших пользователей, избегая длительного процесса код-ревью.
This media is not supported in your browser
VIEW IN TELEGRAM
Про
Если не знаете что-либо из списка ниже - смело открывайте статью. Подойдет больше для подготовки к теоретической части собеседований:
👉 Coroutine
👉 Отличия между
👉 Зачем нужен
Coroutine
и Dispatchers
Если не знаете что-либо из списка ниже - смело открывайте статью. Подойдет больше для подготовки к теоретической части собеседований:
👉 Coroutine
Scope
, Coroutine Context
, Job
👉 Отличия между
Dispatchers.Default
, Dispatchers.Main
, Dispatchers.IO
и Dispatchers.Unconfined
👉 Зачем нужен
newSingleThreadContext
This media is not supported in your browser
VIEW IN TELEGRAM
А вот и пример Server Driven Compose! Внутри:
👉 Классическая архитектура с UDF и разделением по слоям
👉 Модульность
👉 Версионирование компонентов
👉 Стандартный технологический стек Hilt, Compose, ksp, Glide, Coroutines
👉 Экспертиза от GDE по Android и Kotlin
Github: https://github.com/skydoves/server-driven-compose (128⭐️ )
👉 Классическая архитектура с UDF и разделением по слоям
👉 Модульность
👉 Версионирование компонентов
👉 Стандартный технологический стек Hilt, Compose, ksp, Glide, Coroutines
👉 Экспертиза от GDE по Android и Kotlin
Github: https://github.com/skydoves/server-driven-compose (128
Please open Telegram to view this post
VIEW IN TELEGRAM
Хорошая новость прошедшей недели!
Android 15 доступен в AOSP, а значит скоро появится на большем числе девайсов
👉 Улучшили разные API, писал про это тут
👉 Новые шрифты для японского, корейского и китайского языка, и расширение возможностей работы со шрифтами для разработчиков
👉 Более тонкая программная настройка камеры
👉 Изменения в связанные с безопасностью и приватностью
Upgrade assistant уже доступен
Проверить совместимость вашего приложения тут: https://developer.android.com/about/versions/15/reference/compat-framework-changes
Все нововведения тут: https://developer.android.com/about/versions/15
Android 15 доступен в AOSP, а значит скоро появится на большем числе девайсов
👉 Улучшили разные API, писал про это тут
👉 Новые шрифты для японского, корейского и китайского языка, и расширение возможностей работы со шрифтами для разработчиков
👉 Более тонкая программная настройка камеры
👉 Изменения в связанные с безопасностью и приватностью
Upgrade assistant уже доступен
Проверить совместимость вашего приложения тут: https://developer.android.com/about/versions/15/reference/compat-framework-changes
Все нововведения тут: https://developer.android.com/about/versions/15
Исправляем автоимпорт в Android Studio
При автоимпорте на первое место часто всплывают ненужные пакеты.
Например
Как исправить:
👉
👉 Исправить на уровне проекта для всей вашей команды: добавить файл
При автоимпорте на первое место часто всплывают ненужные пакеты.
Например
Modifier
из java.lang.reflect
, а не из compose
или Flow
из java.util.concurrent
, а не из пакета с корутинами Как исправить:
👉
Alt/Opt + Enter
на предлагаемом пакете импорта и нажимаем на Exclude
xxx from completion
👉 Исправить на уровне проекта для всей вашей команды: добавить файл
codeInsightSettings.xml
в .idea вашего проекта и заполнить его ненужными импортами. Ниже пример для стандартного проекта Jetpack Compose + Kotlin Flow + Dagger/Hilt:
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaProjectCodeInsightSettings">
<excluded-names>
<name>android.graphics.Canvas</name>
<name>android.graphics.Color</name>
<name>android.graphics.Paint</name>
<name>android.graphics.drawable.Icon</name>
<name>android.inputmethodservice.Keyboard.Row</name>
<name>android.text.Layout.Alignment</name>
<name>android.view.Surface</name>
<name>android.widget.Button</name>
<name>android.widget.GridLayout.Alignment</name>
<name>androidx.core.content.pm.ShortcutInfoCompat.Surface</name>
<name>java.lang.reflect.Modifier</name>
<name>java.nio.file.WatchEvent.Modifier</name>
<name>java.time.format.TextStyle</name>
<name>javax.annotation.concurrent.Immutable</name>
<name>org.threeten.bp.format.TextStyle</name>
<name>org.w3c.dom.Text</name>
<name>java.util.concurrent.Flow</name>
<name>org.intellij.lang.annotations.Flow</name>
<name>jakarta.inject.Inject</name>
</excluded-names>
</component>
</project>
Немного про синхронизацию в современном kotlin
👉 Для синхронизации 1 значения или конкретной коллекции используем
👉 Для синхронизации
👉 В ином случае
👉 В очень редких случаях когда важен порядок и скорость, а по другому никак - используем
👉 Для синхронизации 1 значения или конкретной коллекции используем
Atomic
/ConcurrentCollections
👉 Для синхронизации
suspend
функций используем Dispatcher
👉 В ином случае
synchronized
👉 В очень редких случаях когда важен порядок и скорость, а по другому никак - используем
Mutex
Разбираемся дает ли val обьекту свойство Immutable
Имутабельность нам нужна для:
👉 Многопоточки
👉 Предстказуемого поведения обьекта и отстутсвия сайд эффектов
👉 Корректного описания состояния обьекта
Что гарантирует нам постоянство ссылки, но не обьекта
Тоже валидный код, где мы переопределяем
Документация нигде не определяет
Задачка со звездочкой. Как создать полностью неизменяемый обьект?
Имутабельность нам нужна для:
👉 Многопоточки
👉 Предстказуемого поведения обьекта и отстутсвия сайд эффектов
👉 Корректного описания состояния обьекта
val
в байт коде выглядит вот так:
private final Ljava/lang/String; text
@Lorg/jetbrains/annotations/NotNull;() // invisible
Что гарантирует нам постоянство ссылки, но не обьекта
Тоже валидный код, где мы переопределяем
val
как var
interface State<out T> {
val value: T
}
interface MutableState<T> : State<T> {
override var value: T
operator fun component1(): T
operator fun component2(): (T) -> Unit
}
val state: State<String> = remember { mutableStateOf("text") }
(state as MutableState<String>).value = "changed"
Документация нигде не определяет
val
как immutable
, только как переменную доступную только для чтенияЗадачка со звездочкой. Как создать полностью неизменяемый обьект?
Самая большая ошибка, которую вы совершили в процессе разработки, и чему она вас научила?
Победитель будет выбран по самому большому числу реакций 15 сентября в 21:00
Please open Telegram to view this post
VIEW IN TELEGRAM
Что посмотреть сегодня вечером? - Фильм про kotlin!
Про то как создавался язык и как так вышло, что Android разработка стала Kotlin first!
Про то как создавался язык и как так вышло, что Android разработка стала Kotlin first!
YouTube
Beyond The Success Of Kotlin / The Documentary by EngX
Almost 15 years ago, a small engineering team at JetBrains embarked on what seemed like a crazy endeavor — to create their own programming language and succeed with it. In the early 2010s, Java was one of the most popular languages, used by millions of engineers.…
👉 Работают над плагинами с поддержкой K2, будет быстрее и стабильнее
👉 Для KMM нас ждет первый релиз Swift Export. Будет лучше работать автокомплит и станет проще создавать новые kmm библиотеки
👉 Ставка на комьюнити! Упрощение процесса разработки и публикации библиотек, инструментов и фреймворков на Kotlin
👉 Дальнейшее улучшение самого языка Kotlin. Улучшение производительности, за счет более чистого кода
Подробнее тут: https://kotlinlang.org/docs/roadmap.html
Please open Telegram to view this post
VIEW IN TELEGRAM
Kotlin Help
Kotlin roadmap | Kotlin