Telegram Group Search
На выходных посмотрел доклад про Declarative Gradle и демку Amper от JetBrains с KotlinConf'24. Вообще, когда только услышал про эти два инструмента, было много вопросов, главные из которых: "Мы только Gradle готовить научились, зачем что-то менять?", "Уже пора переезжать? Если нет, то как подготовиться?" и, конечно, "Какое решение выбрать?". После прочтения доки и просмотра докладов, я нашёл для себя ответы на эти вопросы.

Зачем что-то менять?

Оба проекта направлены на разделение конфигурации сборки (декларативная часть) и логики сборки (императивная часть). Конфигурация остаётся в модулях, а декларативная часть переезжает в плагины. Аргументы такие:

• Для декларативных конфигов проще писать тулинг. Помните случаи когда IDE предлагает добавить или обновить какую-то зависимость? Правда нажимать на эту кнопку не хочется, потому что этот автоматический рефакторинг не знает ничего про специфику проекта и в лучшем случае обновит версию не там, а в худшем сломает сборку. Поддержать в тулинге все возможные варианты объявления зависимостей — нетривиальная задача, которая решается очень просто для декларативных конфигов.

• Ещё один аспект интеграции с тулингом — декларативные конфиги можно парсить "на лету", без компиляции. Открываются возможности для оптимизации. Команда Gradle показала, запуск ./gradlew assemble на проекте с 500 модулями и вариант с декларативной конфигурацией занял 11 секунд против 72 секунд с Kotlin DSL. Скорее всего это какой-то супер простой не-Android проект и, к тому же, они сами говорят, что когда фичей в декларативном варианте будет больше, будет уже не так быстро, но всё равно впечатляет. Стоит воспринимать этот результат как экономию именно на этапе конфигурации.

• Человеку тоже проще читать и понимать декларативные конфиги. Возможно это субъективно, но сложнее понимать что происходит в билд скриптах если логика перемешана с конфигами, так же сложно воспринимается код на Compose где логика не вынесена во ViewModel.

• С декларативным конфигом сложнее "выстрелить себе в ногу", сложнее сделать сборку неэффективной. Просто потому что набор инструментов для этого ограничен. Конечно, такая возможность всё ещё остаётся в декларативной части. Все эти знания про "configuration cache", "configuration avoidance" и т.д. просто не будут нужны для грамотной настройки проекта, но всё ещё пригодятся если хочется написать свой плагин.

Что нам делать сейчас?

Оба проекта в экспериментальном статусе, а значит ещё будут значительные изменения, поэтому в прод тащить пока рано. Но уже можно экспериментировать на небольших проектах или попробовать смигрировать один простой модуль на новый вариант конфигурации (даже не обязательно эту ветку потом вливать). Благо оба инструмента можно использовать поверх сконфигурированного Gradle-проекта. Конечно, в результате экспериментов хорошо бы собрать список пожеланий и отправить их командам.

Если на эксперименты нет времени, можно упростить себе жизнь сейчас и миграцию на декларативный подход в будущем, если приблизить конфигурацию сборки к декларативной. Для этого нужно перенести всю логику в precompiled script plugins или обычные плагины, а в скриптах сборки оставить только настройку этих плагинов.

Какое решение выбрать?

Ответ на этот вопрос будет понятен позднее, но уже видны некоторые отличия. Amper строится с Kotlin Multiplatform в уме, а Declarative Gradle всегда будет лучше интегрирован с Gradle. У Amper больше свободы в решениях, так как он не зависит от Gradle. Как пример, изменение структуры проекта с src/main/kotlin/ на src/. Standalone вариант Amper может дать ещё больше свободы и более тесную интеграцию с IDE.

В общем, следим как будут развиваться эти инструменты. Похоже, что в будущем конфиги сборки будут всё-таки декларативными. Интересно как будет соблюдаться баланс между ограничениями декларативной конфигурации и свободой, которую даёт Gradle.

По теме:
- Репозиторий Amper
- Репозиторий Declarative Gradle
- Why Declarative Gradle is a cool thing I am afraid of: Maven strikes back - краткая история систем сборки и другая точка зрения

#gradle #amper
Ого, в Android Gradle Plugin 8.5.0 оказывается наконец завезли поддержку test fixtures, для модулей на Kotlin. Пока что нужно включать экспериментальный флаг.
Release notes для версии 8.5.0 почему-то пустые, так что узнают только те, кому повезло :D

Test fixtures — любые "приспособления" для применения в тестах. Это могут быть тестовые реализации сущностей, генераторы тестовых данных, специфичные для тестового фреймворка вещи и т.д. В Gradle можно применить плагин test-fixtures к модулю и тогда весь код из source set'а с названием testFixtures будет доступен в тестах этого модуля и для подключения в тесты других модулей. Если у вас есть и Instrumentation, и Unit-тесты, в testFixtures можно сложить общий код. Как и в тестах, в test fixtures доступны internal-сущности из модуля.
Так вот, раньше в обычных Kotlin проектах test fixtures работали, в Android + Java тоже, а в Android + Kotlin — нет.

#agp
Где-то лет пять я пользуюсь fixup'ом через IDE, чтобы добавлять изменения к коммитам из истории, и за это время смирился, что эта фича работает странно. Она просто создаёт коммит, но не ребейзит его автоматически, и приходится через интерактивный ребейз двигать коммит и делать fixup руками. Даже в консольном гите удобнее, там можно при ребейзе указать флаг --autosquash и все коммиты с префиксом fixup! или squash! сами посквошатся как надо. Ну то есть явно какой-то баг в IDE, иначе кнопка "Fixup" почти ничем не отличается от "Interactively Rebase from Here".

И вот спустя годы, когда в очередной раз пригорело от поведения fixup, я решил найти этот баг в YouTrack чтобы влепить звезду. Нашел. И оказалось, что можно было не страдать. Просто IDE учитывает настройку гита rebase.autosquash и если её выставить в true, всё начинает работать как надо 🫠
В комментариях к issue можно наблюдать знатное полыхание на тему "а как я об этом должен был узнать?" и я полностью согласен.

Есть ещё одна ловушка с этой фичей. При коммите нужно обязательно нажать "Commit and Rebase", который спрятан внутри выпадающего списка около кнопки "Commit", иначе магии не произойдёт. Про это, кстати, есть отдельная issue.

👆 TL;DR
Чтобы в IDE не двигать руками коммиты с префиксами fixup! и squash! в нужное место при ребейзе, включи настройку rebase.autosquash:
git config --global rebase.autosquash true


#git #idea
Штош. До свидания Android, может ещё встретимся
Если вдруг хотите обновляться до Gradle 8.10.1 - не надо (не надо x2)
🈁 Сегодня вышел фильм про Котлин
Андрей Бреслав, Роман Елизаров, Дмитрий Жемеров, Светлана Исакова и другие причастные к созданию языка полтора часа рассказывают как это было. Интересно, что заход Kotlin в Android по сути был случайным и мобильные разработчики вообще не были целевой аудиторией :)
(Да, я знаю, что это Telegram, а не Twitter :D)
Please open Telegram to view this post
VIEW IN TELEGRAM
Я считаю, что в релизной версии приложения нужно оставлять логи, и сейчас я вам докажу это на примере из жизни.

Недавно я стал клиентом Generali Srbija (страховая компания) и пользователем их замечательного мобильного приложения. Зарегистрировался, авторизовался и... приложение моментально закрывается когда я его открываю.

Первая догадка — возможно приложению не понравилось, что я отклонил все запросы пермишенов, включая доступ к микрофону, совершению звонков и контактам. Это оказалось ни при чём. Тогда я решил проверить есть ли что-то полезное в logcat (см. скриншот). Логи, которые выводят мой токен, хэш пароля и персональные данные, а так же сообщения типа "Я тут" пропускаем. В конце видим, что приложение само вызывает System.exit(0), написав перед этим "Это настоящая загрузка".

Дальше остаётся только декомпилировать APK через APKTool и поиском найти нужное сообщение лога. Отгадка оказалась простой — если у пользователя обнаружен root, приложение просто закрывается. Кто-то скажет, что это не user-friendly решение, но я парирую цитатой одного мудрого разработчика: "Пользователь всё равно найдёт как сломать приложение, поэтому мы решили не обрабатывать краевые сценарии".

А теперь представьте как было бы сложно понять что происходит, если бы не было никаких логов, приложение бы закрывалось не через System.exit и была включена обфускация! Поэтому если вы не уверены в стабильности приложения, оставляйте логи и отключайте обфускацию. Так пользователи смогут сами подебажить приложение и отправить вам на почту результаты своего исследования, останется только пофискить. Можно было бы ещё флаг debuggable=true выставить, но Google Play такое не пропустит ☹️

Ещё один вывод — если вы ищете работу в Сербии, Generali Srbija отчаянно нуждаются в разработчиках, просто пока этого не поняли.

#security
В общем, я ВНЕЗАПНО понял, что уже вообще-то почти декабрь, а значить вот-вот начнётся новый Advent of Code. Забираю обратно все свои подтрунивания над коммунальными службами, которые "не были готовы к зиме". Я тоже не был готов.

Для новых подписчиков – я уже третий год организую "клуб решал Advent of Code" и мы 25 дней решаем задачи и страдаем получаем положительные эмоции. В этом году организацию начал очень поздно, так что скорее всего состав решающих будет достаточно камерный :)
Заходите в канал @aoc_club и зовите друзей!
Forwarded from Advent of Code Club (Osip Fatkullin)
Please open Telegram to view this post
VIEW IN TELEGRAM
Этого ещё никто не видел, вы первые 👀
Теперь принятие архитектурных решени и проектирование нового функционала для Ktor происходит публично в репозитории ktor-klip!

Первый KLIP на очереди – официальное решение для DI. Все желающие могут посмотреть какой планируется дизайн и повлиять на него на ранней стадии!
Если вы используете Ktor в качестве клиента, эти изменения пока никак вас не коснутся, они направлены на то чтобы упростить использование DI при написании серверов.

А здесь в комментариях можно похоливарить про "зачем ещё один DI?", "почему не Koin?", "будет compile-time валидация графа?". Хотя если прочитать документ, это вопросы отвалятся 😀

#ktor
Пробежавшись по постам за год я понял, что безнадёжно отстаю от трендов. Во-первых я ни разу не поругал дядюшку Боба, а во-вторых ни разу не бомбил про Gradle (предупреждение о сломанном релизе не в счёт). Негоже уходить в новый год с такими пробелами, поэтому буду исправляться. Хотя бы частично.

С дядюшкой Бобом и без меня неплохо справляются, я бы мог разве что на его стороне выступить, но это не модно. А про Gradle мне есть что сказать. Если у вас в этот момент возникла мысль "прочитаю лучше после праздников", подумайте, хотите ли вы начинать год с Gradle.

Существует много причин не любить Gradle, но меня больше всего раздражает его "хрупкость". Есть много способов сделать одно и то же, но только один из них правильный, а остальные приведут к замедлению конфигурации проекта, несовместимости с configuration cache или проект вообще перестанет собираться.

Речь, конечно, про lazy API. Это прям штука про которую нужно знать сразу, как только начинаешь делать в Gradle что-то сложнее чем объявление зависимостей. Но не стоит терять бдительность после прочтения документации. Допустим, ты знаешь, что нужно использовать tasks.named("javadoc") , а tasks.getByName("javadoc") в большинстве случаев не нужно, так как этот вызов создаёт запрошенный таск на месте вместо того чтобы возвращать ленивый провайдер. Но что если нужно сразу сконфигурировать этот таск? Вроде всё просто:
tasks.named("javadoc") { enabled = false }


А если хотим сконфигурировать все таски определённого типа? Можно написать так:
tasks.withType<Javadoc> { enabled = false }

И это будет ошибка. Если в withType передать лямбду, то под капотом вызывается withType<T>().all(configure), а all в моменте создаёт все таски в коллекции. Правильно будет делать так:
tasks.withType<Javadoc>()
.configureEach { enabled = false }


Хорошо, а если хотим выключать таски по какому-то условию? Например, по флажку в gradle.properties:
tasks.withType<Javadoc>().configureEach {
enabled = properties["tasks.javadoc"].toBoolean()
}

Ой-ой, опять ошибка! Нужно использовать findProperty("..."). Почему? Посмотрите документацию к getProperties... а, погодите, там ничего полезного не написано... тогда документацию к Project (скриншот снизу). Этот метод ищет "свойства" в более широком смысле — смотрит на поля convention'ов, Gradle-экстеншены, поля внутри Project, все таски, extras, причём не только для текущего проекта, но и для всех родительских. И все это собирается в одну большую Map'у. Привести это может к довольно неожиданным проблемам.

Так что в новом году желаю вам выбирать всегда правильные APIшки (и не только в Gradle). С Рождеством и Новым Годом :)

#gradle
Начал писать ворчливый комментарий к посту про SOLID, а потом подумал, что у меня ж есть канал, куда можно ворчать. Так что напишу сюда, хотя это немного не формат канала.

Во-первых, конечно, есть уже какое-то чувство усталости от бесконечных статей про SOLID, Clean Architecture и прочие новшества типа ЖЦ Activity.
А во-вторых... ИМХО, проблема всех статей с объяснением SOLID в том, что они пытаются каждый принцип объяснить как можно проще, на элементарных примерах. Чтобы человек посмотрел и сказал: "так SOLID это оказывается просто!". Но в итоге получается, что до применения SOLID было три строки кода, а после стало 10 классов и у читателя возникает только отторжение. Ну потому что дичь. До "рефакторинга" было коротко и понятно.

По сути основная цель SOLID – подстелить себе соломку на будущее, чтобы вносить изменения в существующий код было не "мучительно больно", а хотя бы просто "больно". А чтобы это понять нужно либо самому испытать что получается, когда принципы не соблюдаются, либо посмотреть на реальные примеры из практики, которые должны прям откликаться в сердечке. Такие примеры найти безумно сложно, даже в оригинальных статьях (S O L I D) примеры не всегда удачные. Поэтому, остаётся только пробовать приземлять SOLID на свой опыт, прочитав первоисточник с подробным объяснением принципов и проблем, которые эти принципы призваны решать.
Ох, обычно я не удаляю посты, но в тут шутка вышла из под контроля, поэтому утренний пост дропнул.
Всем хорошего вечера :) Контент скоро будет
Документация меня предупреждала, но я всё равно туда зачем-то полез...
Кто-то скажет, что это очень неудачный нейминг, а это на самом деле гениальное решение! Четыре адреса, как-бы символизируют четыре октета из которых состоит IPv4.

P.S. Два адреса из-за java.net.InetSocketAddress и ещё +1 из-за обёртки в Ktor'овский тип, ну и название локальной переменной +1 🥲
Когда я только начал работать в команде Ktor, я поставил себе "внутреннюю цель" сделать так чтобы использовать Ktor на Android было максимально удобно. И вот в Ktor 3.2.0 благодаря моим правкам Android-проекты перестали собираться 🤡

Причина максимально глупая — пробелы в названиях переменных. Возникает логичный вопрос: "Зачем?". А просто потому что когда-то я увидел подобный подход для компановки сообщений в Dokka и подумал "вау, как выразительно получается".
Перед тем как применить это в коде я подумал может ли это что-то сломать, и пришёл к выводу, что нет. Это ж константы, они при компиляции заинлайнятся и вообще использований не будет, что может пойти не так? То что D8 споткнётся о пробел я, конечно, не предусмотрел.

Так что вы знаете кого винить, что не получается обновиться на новый Ktor. А я пойду думать как на CI гонять проверки, что проекты с Ktor собираются с D8, R8 и ProGuard.

P.S. Workaround нашёлся, но достаточно страшный. Через AGP Transformation API вырезать проблемные поля 🙈
2025/06/13 11:37:55
Back to Top
HTML Embed Code: