Ra'Reilly - Заметки про Ktor и не только – Telegram
Ra'Reilly - Заметки про Ktor и не только
975 subscribers
71 photos
133 links
Каждую неделю (нет) тут появляются заметки.
В основном про около-Ktor, но иногда и про тулинг залетает.

Автор: @osipxd
Download Telegram
KSP (Kotlin Symbol Processing) вышел в альфу.
Это новый API для кодогенерации от Google на замену KAPT. Обещают, что он в два раза быстрее чем KAPT и гугловские либы с кодгеном будут переезжать на KSP.
Уже есть экспериментальная поддержка KSP для Moshi, но на этом пока что всё.

Подробнее про то, что это за покемон и как его пощупать читайте в официальном анонсе.

#kotlin #codegen
Если вы собираетесь мигрировать на Maven Central, вы наверняка знаете, что на создание репозитория нужно заводить issue в JIRA Sonatype. Так вот важная деталь - нужно создавать одну issue на верхнеуровневый пакет, а не для каждой либы.
Например, есть у вас либы с пакетами:
- my.domain.foo
- my.domain.bar
Нужно создать issue только для пакета my.domain и подтвердить, что вы владелец домена domain.my.
Так что всё не так страшно как мне казалось изначально и не нужно заводить 100 issue, если у вас 100 библиотек.

#publish
Совет, который сохранил бы мне день. Будьте осторожны с конструкцией:

fragment.viewLifecycleOwnerLiveData.observe(fragment) { lifecycleOwner ->
lifecycleOwner.lifecycle.addObserver(...)
}

Две вещи про которые нужно помнить, чтобы понять в чём проблема:
1. Подписка на LiveData станет активной только после того как fragment перейдёт в состояние STARTED, то есть после метода onStart, т.к. используется LiveData.observe(LifecycleOwner)
2. onViewCreated может быть вызван без onStart, если вьюха создалась, но фрагмент не отобразился пользователю. Например, если вы переходите на следующий экран до того как текущий фрагмент успел отобразиться.
(У меня такое поведение воспроизвелось ещё и при восстановлении бэкстэка после перехода по диплинку)

В сумме получаем, что если у фрагмента вызывается onCreate - onViewCreated - onDestroyView, в подписке на viewLifecycleOwner мы об этом не узнаем. Если нужно чистить что-то при каждом уничтожении вьюхи фрагмента, это может быть критично.

На практике я поймал это поведение при использовании viewbinding-ktx. Из-за этого получался неприятный баг при переходе по диплинку.
1. У промежуточного фрагмента вызывается onViewCreated, в нём мы используем binding и он кэшируется.
2. Вызывается onDestroyView, но очистка биндинга не срабатывает потому что фрагмент ещё не перешёл в состояние STARTED и мы ещё не подписались на ЖЦ вьюхи
3. При следующем создании вьюхи фрагмента биндинг не пересоздаётся и мы используем биндинг со сылками на старые вьюхи.
4. Вместо нормального инициализированного экрана видим какую-то ерунду, скорее всего пустой экран.

Один из вариантов решения — использовать observeForever вместо observe, чтобы не привязываться к ЖЦ фрагмента.
В версии viewbinding-ktx 4.1.2-2 я исправил эту проблему немного по другому.

#bug #library #lifecycle
Красивая картинка из доки вдогонку к прошлому посту про работу с ЖЦ. А заодно ещё одно наблюдение.

Если нужно во фрагменте выполнить suspend-функцию, которая влияет на UI, лучше использовать
lifecycleScope.launchWhenStarted { ... }
а не
lifecycleScope.launchWhenResumed { ... }
т.к. состояния STARTED уже достаточно, чтобы работать с UI.

Опять история про диплинки. Поймал случай, когда при открытии экрана по диплинку код в launchWhenResumed не выполняется т.к. фрагмент остаётся в состоянии CREATED и не переходит в состояние RESUMED. Пока не разобрался из-за чего так происходит, но похоже на баг, т.к. в норме onResume у фрагмента вызывается.

Если знаете почему может быть такое поведение, напишите в комменты.

#lifecycle
Выпустил обновление 2.0-rc1 для mapmemory.
Эта библиотека позволяет быстро реализовать in-memory кэш.

Полностью release notes пересказывать не буду, но вот что хочется отметить:
- Теперь значения в MapMemory могут быть scoped и shared. Scoped ограничены к использованию только внутри класса где объявлены, shared можно шарить межу классами.
- Новый модуль mapmemory-test с утилитами для удобного тестирования классов, использующих MapMemory.
- Добавил поддержку RxJava 3
- Переписал README и KDoc комментарии. Жду комментов всё ли понятно теперь :)
- Упростил синтаксис для объявления nullable полей и полей со значением по умолчанию.

Гайд по миграции с v1.1 добавил в README.

#library
Пошёл на выходных обновить mapmemory, но споткнулся о баг в котлине и не дошёл.

Забавно в этом баге то как я пытался его воспроизвести, потому что он стрелял только в боевом проекте. Алгоритм воспроизведения довольно специфичный, поэтому в чистом проекте за несколько попыток воспроизвести не удалось.

Чтобы уж наверняка, скопировал весь рабочий проект и постепенно удалял из него всё лишнее, периодически пересобирая проект, чтобы убедиться, что баг не пропал. Получился около-бинарный поиск.
В итоге осталось три файлика и правильные настройки из которых смог слепить тестовый проект.

#bug #kotlin
Если вы с одного устройства коммитите и в рабочий git-репозиторий, и в личный, наверняка были случаи, когда закоммитили код не с той почтой и именем.
И вот запушил что-то в рабочий проект с личного аккаунта, а потом сидишь и вычищаешь это через rebase перфекционизму ради.

Простой способ этого избежать — после того как стянул себе проект настроить всё что надо через git config. Если проектов много, а нужно настроить не только имя и email, но и ключ для подписи, например, это не удобно.

Более универсальный способ — использовать в глобальном .gitconfig настройку includeIf. Как можно понять из названия, эта настройка применит конфиги из другого файла, если выполняется условие. Синтаксис для определения условий достаточно ограниченный, но можно проверить в какой папке находится проект и в зависимости от этого применить настройки. У себя использую эту штуку так:

[includeIf "gitdir:~/dev/rmr/"]
path = .gitconfig.rmr

В этом случае настройки из общего .gitconfig применяются ко всем проектам, а для проектов из папки dev/rmr/ некоторые настройки переопределяются в файле .gitconfig.rmr

#git
Можете считать, что меня только что разморозили, но я только вчера узнал про git subtree.
Это альтернатива git submodule. Как и submodule, subtree позволяет подтянуть другой репозиторий внутрь вашего, но отличается подход.

submodule оставляет "метку", что "в этой папке должна лежать такая-то ревизия такого-то репозитория". Физически файлов из другого репозитория в вашем репозитории нет и чтобы они появились, нужно выполнить определённую команду. Наверное, это больше всего отпугивает от submodule — чтобы получить полностью рабочий репозиторий, недостаточно его клонировать, нужно ещё выполнить дополнительные действия.

subtree физически добавляет файлы из другого репозитория в ваш репозиторий вместе с историей коммитов. Процесс клонирования репозитория никак не меняется и другие разработчики могут не знать, что в проекте есть subtree. Файлы из "поддерева" можно изменять так же как с остальные файлы проекта

Про submodule должны знать все разработчики, а знать о subtree нужно только в момент когда хочется стянуть изменения из удалённого репозитория (pull) или наоборот запушить туда свои (push).

# Добавляем удалённый репоизторий для subtree
$ git remote add --fetch <repo_name> <url>

# Стягиваем его в нужную папку
$ git subtree add --prefix <dir> <repo_name> <ref>

# Ну и дальше можем делать fetch, pull, push
$ git subtree fetch --prefix <dir> <repo_name> <ref>
$ git subtree pull --prefix <dir> <repo_name> <ref>
$ git subtree push --prefix <dir> <repo_name> <ref>

P.S. Осталось научиться нормально синхронизировать файлы выборочно между репозиториями и будет совсем хорошо. Буду конфиги шарить направо и налево.

#git
Сегодня убил кучу времени на баг которого не существует.

В апишке есть метод, который возвращает 204 No Content. Тестировщик подменил код ответа на 500, чтобы проверить как приложение реагирует на ошибку и в результате получилось такое поведение:
→ Запрос улетает
← Приходит ответ с кодом 500 (видно по логам)
... Приложение чего-то ждёт и запрос отваливается по таймауту.
Что? Да. Запрос, ответ на который уже вроде как пришёл отваливается по таймауту.

Отгадка отказалась проста. Ответ 204 не подразумевает тела и в нём нет заголовка Content-Length, а вот для 500 тело может прийти и Content-Length должен быть определён. В результате у HTTP-клиента ломается мозг и он до таймаута ждёт пока ему долетят остатки ответа, которых не существует.

Мораль — перед тем как чинить, убедитесь, что оно сломано :) А если серьёзно, то внимательнее смотрите на запросы и ответы которые тестировщики делают частичной подменой данных.
А теперь к важным новостям!
На GitHub поменяли цвет для Kotlin и теперь его не перепутать со Swift.
Московские роботы-андроиды* выкатываются на YouTube!
Дебютное видео про всеми любимый Gradle.

Первые 50 минут теория, где:
- Разбираем какие могут возникнуть проблемы с Groovy у Java/Kotlin разработчика
- Смотрим как мигрировать с Groovy DSL на Kotlin DSL
- Кратко пробегаемся по основным APIшкам Gradle которые нужно знать, чтобы перейти ко второй части доклада.

Жанр второй части я бы назвал "косячный live coding", потому что это не тот live coding когда у докладчика всё отрепетировано и нет никаких проблем, а когда вы пробуете что-то написать самостоятельно, тут же огребаете. Тут собраны все косяки которые скорее всего соберёт человек, который впервые пишет плагины под Gradle.

Вторая часть длится полтора часа и в ней:
- Пишем Precompiled Script Plugin (после того как везде исправлял это название на монтаже я выучил как правильно!)
- Пишем плагин, с возможностью конфигурирования
- Пишем задачку (task). Делаем, чтобы в неё работало кэширование и создаём цепочки из задач

И это всё на близком к реальности Android-проекте.
Кстати, это один из докладов с нашей ежегодной робопрактики мобильных разработчиков.
Как говорится, подписывайтесь, ставьте лайки, рассказывайте друзьям :) Если понравится, будем продолжать.

* Мы в red_mad_robot не обижаемся, когда нас называют роботами, а даже наоборот сами себя так называем

#анонс #tooling
Оказывается, плагин kotlin-android (и другие kotlin-плагины) по умолчанию добавляют в зависимости stdlib. Такое поведение появилось начиная с версии 1.4.

Причём работает эта штука достаточно умно — она в зависимости от вашего "окружения" выбирает что подключать: stdlib-jdk8, stdlib-common или вообще stdlib-js.
НО если вы руками объявили зависимость stdlib, она уже не будет добавляться автоматически.

Конечно, оставили флажок на случай если вы не хотите полагаться на эту логику, а хотите просто отключить автоматическое добавление stdlib. В gradle.properties нужно добавить такую строчку:

kotlin.stdlib.default.dependency=false

Ещё один интересный момент — версия для автоматически добавляемой зависимости берётся из свойства kotlin.coreLibrariesVersion. В это свойство по умолчанию записывается версия подключённого kotlin-gradle-plugin, но её можно перезаписать.
Благодаря этим строчкам версия из kotlin.coreLibrariesVersion используется для всех зависимостей org.jetbrains.kotlin.
Ещё один способ выбрать общую версию Kotlin для всего проекта.

#kotlin #gradle
Всё. Теперь не работают отговорки типа "на Android не принято заголовок по центру тулбара делать", "это системное поведение, его нельзя поменять". Теперь можно.
#ui
Через неделю - 27-го июля с утра буду выступать на Podlodka Android Crew с рассказом про Git.
Планирую рассказать много всего - про чистоту истории, атомарные коммиты, разные модели ветвления, как всё-таки заставить всех разработчиков следовать договорённостям. Еще хочу обзорно рассмотреть полезные функции - cherry-pick, patch, stash и т.д. и как это всё ложится на Android Studio (читай IntelliJ IDEA).

Думаю, что доклад будет слабо связан с Android, поэтому может быть интересен не только андроид-разработчикам. Ссылка на запись доклада появится на этом канале :)

#анонс #git
Чтобы было не скучно ждать пока мы зальём что-то ещё на канал, появился плейлист "Это тоже мы". Туда я складываю выступления с площадок вне нашего канала (пока что все, хе-хе).

Как и обещал, добавил в этот плейлист ссылку на свой доклад про Git с подлодки. Там доступ по ссылке, поэтому если вы не купили билет, то посмотреть можно пока только с нашего канала.

В общем, по классике - подписывайтесь, ставьте лайки, рассказывайте друзьям и вот это всё :)

#rmr #анонс
Просматривал исходники AGP 7.0.0 и заметил, что теперь папка kotlin по умолчанию добавлена в source-set и больше не нужно делать это вручную, если подключён Kotlin Gradle Plugin.
Вот этот коммит.
В release notes AGP этого изменения не нашел.

#kotlin #gradle
На нашем канале пока тихо, зато Саша Серебренникова (@serebrennik) записала для @android_broadcast аж сразу серию видео про Custom View! Я уже посмотрел два из трёх видео и всем советую — Саша нереально крутая. Наконец материал с нормальными примерами, а не "нарисуем зелёный треугольничек с красным квадратиком".

Ссылочку на плейлист уже добавил на канал, чтобы все наши выступления можно было найти в одном месте :)

#ui #анонс
Так-так-так. Material 3 - это что за покемон?

#ui