Если вы использовали Gradle Version Catalogs, наверняка видели такую ошибку в каждом build-скрипте. Чтобы от неё избавиться даже плагин написали, ну а простые работяги добавляли
Так вот, свершилось! Эту проблему пофиксили и фикс войдёт в Gradle 8.1 🎉
Разберёмся в чём был баг👇
@Suppress("DSL_SCOPE_VIOLATION") в каждый build-скрипт.Так вот, свершилось! Эту проблему пофиксили и фикс войдёт в Gradle 8.1 🎉
Разберёмся в чём был баг👇
❤2
Ребята из Gradle не сразу поняли в чём проблема - код светится красным, но при этом всё собирается и работает. Логично предположить, что баг на стороне IntelliJ. Ребята из JetBrains исследовали проблему и выявили что она всё-таки на стороне Gradle, т.к. действительно есть нарушение DSL скоупа, Kotlin выдаёт верную ошибку и каталоги вообще не должны были работать внутри блока
Почему не должны были?
Дело в том, что блок
Аксессоры для каталогов генерируются как экстеншены на
В Gradle 8.1 будут генерироваться легальные аксессоры для доступа к каталогам внутри блоков
Очередная история про то как важно понимать причину проблемы. Баг зарепортили полтора года назад — в августе 2021, а пофиксили в течении трёх месяцев после того как стало понятно где именно проблема.
Мораль: если вы репортите issue и у вас есть возможность самостоятельно провести исследование проблемы, шансы на быстрый фикс возрастают. Ваш кэп.
UPD: Что-то пошло не так и фикс будет в 8.2 Пронесло
#gradle #bug
plugins.Почему не должны были?
Дело в том, что блок
plugins особенный. Попробуйте на верхнем уровне build-скрипта объявить переменную, а потом использовать её внутри plugins, Kotlin её просто не увидит. Если посмотреть на описание класса PluginDependenciesSpecScope, там явно написано:This class exists for the sole purpose of marking the plugins block as a GradleDsl thus hiding all members provided by the outer KotlinBuildScript scope.То есть не должно быть возможности внутри блока
plugins использовать ничего извне.Аксессоры для каталогов генерируются как экстеншены на
Project, то есть и они не должны были быть доступны внутри блока plugins. Но всё-таки они работали т.к. добавлялись через provided properties, механизм в Kotlin noscripting. Плагин Kotlin, видимо, не учитывает "provided properties" и поэтому высвечивает ошибку в IDE.В Gradle 8.1 будут генерироваться легальные аксессоры для доступа к каталогам внутри блоков
plugins и buildnoscript. Помимо того что это позволяет исправить нарушение DSL скоупа, это позволяет ещё и не генерировать аксессоры для бандлов и библиотек внутри plugins, т.к. их там всё равно нельзя использовать. Правда для обратной совместимости они всё равно генерируются, но с аннотацией @Deprecated.Очередная история про то как важно понимать причину проблемы. Баг зарепортили полтора года назад — в августе 2021, а пофиксили в течении трёх месяцев после того как стало понятно где именно проблема.
Мораль: если вы репортите issue и у вас есть возможность самостоятельно провести исследование проблемы, шансы на быстрый фикс возрастают. Ваш кэп.
#gradle #bug
🔥18
Обычно мы на всех проектах добавляем тип сборки QA. Это такая сборка, которая максимально близка к релизной, но может включать в себя дебаг-панель и позволяет подключиться к приложению через дебаггер.
И вот недавно оказалось, что схема не работает. QA сборки не обфусцируются, а значит не сильно отличаются от дебажных. Причём не понятно в какой момент отвалилась обфускация и из-за чего это произошло.
Расследование привело к багтреккеру гугла: "AGP 7.2.0 onwards does not obfuscate our built app" и issue закрыто как Won't fix (Intended behavior)🙂
Дело в том, что флаг
Решение подсказали там же в issue. Новый API, добавленный в AGP7.0 4.2 позволяет вклиниться в процесс подготовки артефактов сборки. Можно подменять, добавлять и изменять промежуточные файлы, например: манифест, APK и даже использовать совсем чёрную магию — модифицировать уже скомпилированные классы. Хотелось когда-нибудь модифицировать один класс в библиотеке, чтобы не ждать исправления бага?
Но сейчас не об этом. Нам интересен манифест. С помощью этого API мы можем дописать любой текст в смердженный манифест, в частности и флаг
👨🦽 Результат можно увидеть в фиксе для gradle-infrastructure или в отдельном сниппете.
P.S. Помимо флага в манифесте приходится ещё руками менять значение
#agp #snippet
И вот недавно оказалось, что схема не работает. QA сборки не обфусцируются, а значит не сильно отличаются от дебажных. Причём не понятно в какой момент отвалилась обфускация и из-за чего это произошло.
Расследование привело к багтреккеру гугла: "AGP 7.2.0 onwards does not obfuscate our built app" и issue закрыто как Won't fix (Intended behavior)
Дело в том, что флаг
isDebuggable = true теперь отключает обфускацию и по задумке инженеров гугла так должно было работать всегда. Ведь вы не сможете бегать по коду дебаггером, если он отличается от исходников, какой же это тогда получается debuggable? То что раньше была возможна обфускация с isDebuggable = true это изначально ошибочное поведение и AGP никогда не должен был так работать. Видимо ошибка показалась настолько очевидной, что фикс даже не упомянули в release notes, а как известно every change breaks someones workflow, что и случилось.Решение подсказали там же в issue. Новый API, добавленный в AGP
Но сейчас не об этом. Нам интересен манифест. С помощью этого API мы можем дописать любой текст в смердженный манифест, в частности и флаг
debuggable.P.S. Помимо флага в манифесте приходится ещё руками менять значение
BuildConfig.DEBUG ведь он тоже зависит от isDebuggable.#agp #snippet
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12😁2🔥1
Насмотренность — важный навык в работе дизайнера. Это то что помогает быть в тренде, учиться на чужом опыте, черпать вдохновение, отличать хороший дизайн от плохого, критически оценивать свою работу и так далее. Нет вы не ошиблись каналом, просто я считаю, что насмотренность важна и разработчикам. Чем больше чужого кода смотришь — тем лучше пишешь свой. Просто потому что больше новых приёмов и концепций узнаёшь, улучшаешь свой скилл владения языком. С хэштегом #насмотренность буду писать про всё интересное на что наткнулся при просмотре open-source проектов. Это первый пост :)
👀 Companion object
Чаще всего
☝️ Json в kotlinx.serialization
Это класс, но мы можем использовать его как будто он
Всё потому что
Зачем так сложно, если можно было бы просто объявить
✌️CoroutineContext.Key в kotlinx.coroutines
CoroutineContext вообще интересная штука. По сути это что-то между Map и Set. Каждый элемент контекста хранится в
Каждый ключ должен быть singleton'ом и ключ должен быть у каждого элемента контекста (
У каждого элемента контекста
Выглядит как-будто получаем элемент контекста по типу. На самом деле используется
👀 Companion object
Чаще всего
companion object используется просто как замена статическим методам и полям из Java. А ведь companion object это в первую очередь object и как у любого другого объекта у него может быть имя и он может реализовывать интерфейсы и наследоваться от других классов. Сегодня будет два примера таких companion object'ов.☝️ Json в kotlinx.serialization
Это класс, но мы можем использовать его как будто он
object:val result = Json.decodeFromString("42")Всё потому что
companion object у Json реализует класс Json:companion object Default : Json(JsonConfiguration(), EmptySerializersModule())
Зачем так сложно, если можно было бы просто объявить
object Json? Потому что тогда не было бы возможности создать свой инстанс, а когда Json это класс, такая возможность остаётся.✌️CoroutineContext.Key в kotlinx.coroutines
CoroutineContext вообще интересная штука. По сути это что-то между Map и Set. Каждый элемент контекста хранится в
CoroutineContext по ключу и получить элементы контекста можно через оператор get.Каждый ключ должен быть singleton'ом и ключ должен быть у каждого элемента контекста (
Job, CoroutineDispatcher и т.д.). Идеальная задача для companion object!У каждого элемента контекста
companion object это реализация CoroutineContext.Key и смотрите какая красота получается в совокупности с оператором get у CoroutineContext:val job = coroutineContext[Job]
val dispatcher = coroutineContext[CoroutineDispatcher]
Выглядит как-будто получаем элемент контекста по типу. На самом деле используется
companion object c типом Key, объявленный в этих классах. Например, в Job он выглядит так:companion object Key : CoroutineContext.Key<Job>
👍9🔥4
Я в прошлом году писал про минимальный размер элементов вёрстки, чтобы на них было легко нажимать. В compose для соблюдения этого правила даже был
Так вот в compose 1.4.0 его переименовали в
Modifier.minimumTouchTargetSize, который использовался в Material компонентах, но почему-то был internal.Так вот в compose 1.4.0 его переименовали в
Modifier.minimumInteractiveComponentSize и сделали публичным 🎉Telegram
Ra'Reilly - Заметки про Android и не только
Когда я только начинал использовать Material, я заметил, что кнопки в вёрстке располагаются неправильно. Вроде отступы выставляю как в дизайне, но они почему-то получаются больше. Оказалось у кнопки есть дополнительные отступы по 6dp снизу и сверху. Что я…
👍10❤3
За последние две недели вышло много крупных обновлений — Kotlin 1.8.20, AGP 8.0.0, Gradle 8.1 и новая Android Studio. Я пообещал себе не превращать канал в поток сообщений об обновлениях, но иногда изменения цепляют сердечко и я не могу устоять перед соблазном написать про них.
В этот раз порадовал Gradle.
👉 Во-первых, поддержку version catalogs в блоке
👉 Во-вторых, в Kotlin DSL завезли поддержку оператора = для lazy properties. То есть теперь можно писать так:
Чтобы это всё заработало понадобится:
• Kotlin 1.8.20 (на самом деле достаточно 1.8.0, но в последней версии пофиксили важные баги)
• IntelliJ IDEA 2022.3+ (или Android Studio Giraffe)
• добавить в
В Groovy DSL, к слову, так можно было делать всегда. Но на то он и Groovy, чтобы делать всякую магию, нам же интересно как это работает в Kotlin.
После компиляции строка
За это превращение отвечает плагин компилятора assignment-plugin. Он позволяет пометить класс специальной аннотацией и у такого класса можно будет "переопределить" оператор присваивания.
Официальной документации на плагин нет, но если захотите применить у себя в проекте, я написал небольшую доку с примером применения, требованиями и ограничениями, которые удалось собрать из исходников. Например, для меня стало сюрпризом, что переопределённый оператор присваивания нельзя использовать с локальными переменными или параметрами функции. Из-за этого ограничения долго не мог понять почему плагин у меня не работает.
Возможно это не так, но есть ощущение, что плагин написан специально для Gradle. К тому же, Kotlin Gradle Plugin недавно переехал на lazy properties и такая фича будет как нельзя кстати, чтобы упростить миграцию для пользователей.
❓Использовать или нет?
Да. Может пугать то, что фича в экспериментальном статусе, но если вы используете свежий Gradle, Kotlin и IDE, не вижу причин не использовать.
Сам Gradle уже применяет assignment operator в своём проекте.
А если вы автор плагинов, у вас не осталось причин не использовать lazy properties, тем более что теперь пользователи даже не заметят этого и не увеличится разница между Kotlin и Groovy DSL.
Вообще, рекомендую прочитать список изменений Gradle 8.1, там ещё configuration cache стабилизировали, сделали более полезными сообщения об ошибках компиляции Kotlin DSL и т.д.
#gradle #kotlin
В этот раз порадовал Gradle.
👉 Во-первых, поддержку version catalogs в блоке
plugins {}, про которую я недавно писал, всё-таки включили в релиз 🎉👉 Во-вторых, в Kotlin DSL завезли поддержку оператора = для lazy properties. То есть теперь можно писать так:
redmadrobot {
android {
// Вместо такого
minSdk.set(28)
// Вот так
minSdk = 28
}
}
Чтобы это всё заработало понадобится:
• Kotlin 1.8.20 (на самом деле достаточно 1.8.0, но в последней версии пофиксили важные баги)
• IntelliJ IDEA 2022.3+ (или Android Studio Giraffe)
• добавить в
gradle.properties строчку systemProp.org.gradle.unsafe.kotlin.assignment=true
🪄 Как это работает в Kotlin?В Groovy DSL, к слову, так можно было делать всегда. Но на то он и Groovy, чтобы делать всякую магию, нам же интересно как это работает в Kotlin.
После компиляции строка
minSdk = 28 превращается в minSdk.assign(28), а реализация assign выглядит так:fun <T> Property<T>.assign(value: T?) {
emitIncubatingLogMessage()
this.set(value)
}
За это превращение отвечает плагин компилятора assignment-plugin. Он позволяет пометить класс специальной аннотацией и у такого класса можно будет "переопределить" оператор присваивания.
Официальной документации на плагин нет, но если захотите применить у себя в проекте, я написал небольшую доку с примером применения, требованиями и ограничениями, которые удалось собрать из исходников. Например, для меня стало сюрпризом, что переопределённый оператор присваивания нельзя использовать с локальными переменными или параметрами функции. Из-за этого ограничения долго не мог понять почему плагин у меня не работает.
Возможно это не так, но есть ощущение, что плагин написан специально для Gradle. К тому же, Kotlin Gradle Plugin недавно переехал на lazy properties и такая фича будет как нельзя кстати, чтобы упростить миграцию для пользователей.
❓Использовать или нет?
Да. Может пугать то, что фича в экспериментальном статусе, но если вы используете свежий Gradle, Kotlin и IDE, не вижу причин не использовать.
Сам Gradle уже применяет assignment operator в своём проекте.
А если вы автор плагинов, у вас не осталось причин не использовать lazy properties, тем более что теперь пользователи даже не заметят этого и не увеличится разница между Kotlin и Groovy DSL.
Вообще, рекомендую прочитать список изменений Gradle 8.1, там ещё configuration cache стабилизировали, сделали более полезными сообщения об ошибках компиляции Kotlin DSL и т.д.
#gradle #kotlin
Telegram
Ra'Reilly - Заметки про Android и не только
Ребята из Gradle не сразу поняли в чём проблема - код светится красным, но при этом всё собирается и работает. Логично предположить, что баг на стороне IntelliJ. Ребята из JetBrains исследовали проблему и выявили что она всё-таки на стороне Gradle, т.к. действительно…
👍10
Оказалось, что фича с адаптивными emoji, которые я исопльзовал в предыдущем посте, поддерживается не во всех клиентах. Я конечно завёл баги (macOS, Web K, Web Z), но хочется понять сколько людей затронула проблема (статистики по клиентам у меня нет).
Final Results
33%
Работает - все emoji-логотипы под цвет текста
67%
Не работает (клиент для macOS)
0%
Не работает (Web)
Docker-образа для сборки Android-приложений это какой-то ужас. Так я начал этот пост в прошлый понедельник, а потом пошёл за примерами "ужаса" и... застрял на неделю потому что:
- Во-первых, всё уже гораздо лучше чем было 3 года назад, когда я последний раз искал подходящий docker-образ
- Во-вторых, мои представления о "хорошем образе" немного устарели и образа могут быть ещё лучше 👇
- Во-первых, всё уже гораздо лучше чем было 3 года назад, когда я последний раз искал подходящий docker-образ
- Во-вторых, мои представления о "хорошем образе" немного устарели и образа могут быть ещё лучше 👇
🤨 Какие проблемы есть у Docker-образов с Android SDK?
Они тяжёлые.
Чаще всего публичные образа собираются по принципу "это тоже может понадобиться, и вот это надо добавить". Даже "минимальный образ" обычно содержит около 5 версий Android, ещё пяток версий build-tools, NDK, cmake, эмулятор и вот уже как минимум 5 гигов занято. А чтобы образ подошёл всем, можно докинуть ещё Node.js, Flutter SDK и Ruby с Fastlane 👍
Вес образов не проблема до тех пор пока ты их просто используешь as-is. Но в один прекрасный момент появляется потребность что-то добавить в образ, чтобы не ставить это "что-то" перед каждой сборкой и сэкономить на этом время.
Думаешь "возьму текущий образ как базовый и на его основе наклепаю свой", пишешь Dockerfile, пробуешь собрать локально и... Бац! Место кончилось — ну ничего, почистим диск. Бац! На M1 образ не собирается. И вот ты уже читаешь как собрать образ под определённую платформу и всё глубже погружаешься в недра докера.
Позже хочется ещё что-то поменять и становится понятно, что если хочешь образ в котором будет только то, что тебе нужно, собери его сам. Этот путь не легче и не в последнюю очередь потому что не понятно что нужно, а что нет, это следующая проблема.
Google не помогает.
Я не про поисковик, а про команду Android.
Эталонный официальный пример Docker-образа закрыл бы многие вопросы. Но такого образа нет и чем глубже ты погружаешься в тему создания своего образа, тем больше странностей и вопросов встречаешь:
👉 Зачем нужен каждый пакет в sdkmanager? Где взять их описание?
👉 Кто-нибудь знает для чего в Docker-образа ставят пакеты
👉 Почему
👉 Блин. Сборка падает потому что
👉 Надо ли ставить несколько версий
👉 Надо ли ставить
👉 Какие переменные окружения надо выставлять? Так... Надо выставить
👉 Надо ли устанавливать 32-битные версии библиотек? Хм.. Ничего не написано про то зачем они нужны, но кажется они нужны только для Android Studio. Убрал — ничего не сломалось.
Не всегда есть достаточное количество времени чтобы с этим всем разбираться, тем более если поддержка Dockerfile это не твоя прямая обязанность.
К сожалению, я не нашёл какого-то минимального образа, на базе которого можно было бы сделать свой образ не углубляясь во все эти дебри, поэтому —
Стараюсь пояснять все неочевидные моменты комментами и в целом сохранять Dockerfile читаемым. Если вдруг что-то там не понятно — оставляйте комментарии к коммитам или открывайте issue.
Если всё-таки нужен образ, который может пережевать всё, рекомендую посмотреть на mingchen/docker-android-build-box.
#ci
Они тяжёлые.
Чаще всего публичные образа собираются по принципу "это тоже может понадобиться, и вот это надо добавить". Даже "минимальный образ" обычно содержит около 5 версий Android, ещё пяток версий build-tools, NDK, cmake, эмулятор и вот уже как минимум 5 гигов занято. А чтобы образ подошёл всем, можно докинуть ещё Node.js, Flutter SDK и Ruby с Fastlane 👍
Вес образов не проблема до тех пор пока ты их просто используешь as-is. Но в один прекрасный момент появляется потребность что-то добавить в образ, чтобы не ставить это "что-то" перед каждой сборкой и сэкономить на этом время.
Думаешь "возьму текущий образ как базовый и на его основе наклепаю свой", пишешь Dockerfile, пробуешь собрать локально и... Бац! Место кончилось — ну ничего, почистим диск. Бац! На M1 образ не собирается. И вот ты уже читаешь как собрать образ под определённую платформу и всё глубже погружаешься в недра докера.
Позже хочется ещё что-то поменять и становится понятно, что если хочешь образ в котором будет только то, что тебе нужно, собери его сам. Этот путь не легче и не в последнюю очередь потому что не понятно что нужно, а что нет, это следующая проблема.
Google не помогает.
Я не про поисковик, а про команду Android.
Эталонный официальный пример Docker-образа закрыл бы многие вопросы. Но такого образа нет и чем глубже ты погружаешься в тему создания своего образа, тем больше странностей и вопросов встречаешь:
👉 Зачем нужен каждый пакет в sdkmanager? Где взять их описание?
👉 Кто-нибудь знает для чего в Docker-образа ставят пакеты
extra;m2repository? Для чего скачивать локальный maven репозиторий, если мы его никак не подключаем в Gradle? Может AGP его подключает? Я тоже так подумал, но не нашёл этому никаких подтверждений. Убрал установку этих пакетов — ничего не сломалось 🤷👉 Почему
build-tools тянет за собой emulator? А, по ошибке видимо. Начиная с 31.0.0 уже не тянет.👉 Блин. Сборка падает потому что
llvm-rs-cc не может найти libncurses5. А, эту проблему тоже поправили начиная с build-tools 31.0.0👉 Надо ли ставить несколько версий
build-tools или можно поставить только самую новую? Есть ответ на SO, что версии обратно совместимы, но не звучит как авторитетный источник, да? Changelog крайне скупой, а реально полезную информацию можно найти разве что в исходниках. Для себя решил, что лучше использовать последнюю версию по причине из прошлых пунктов.👉 Надо ли ставить
platform-tools? Кажется что для сборки они не нужны, но AGP использует adb для всякого, так что подразумевает, что platform-tools установлены.👉 Какие переменные окружения надо выставлять? Так... Надо выставить
ANDROID_HOME... а погодите, она deprecated. Вместо неё надо выставлять ANDROID_SDK_ROOT. Стоп, она deprecated, и вместо неё надо выставлять... ANDROID_HOME. Ок.👉 Надо ли устанавливать 32-битные версии библиотек? Хм.. Ничего не написано про то зачем они нужны, но кажется они нужны только для Android Studio. Убрал — ничего не сломалось.
Не всегда есть достаточное количество времени чтобы с этим всем разбираться, тем более если поддержка Dockerfile это не твоя прямая обязанность.
К сожалению, я не нашёл какого-то минимального образа, на базе которого можно было бы сделать свой образ не углубляясь во все эти дебри, поэтому —
android-sdk:base в RedMadRobot/android-docker-images это как раз такой образ.Стараюсь пояснять все неочевидные моменты комментами и в целом сохранять Dockerfile читаемым. Если вдруг что-то там не понятно — оставляйте комментарии к коммитам или открывайте issue.
Если всё-таки нужен образ, который может пережевать всё, рекомендую посмотреть на mingchen/docker-android-build-box.
#ci
GitHub
GitHub - RedMadRobot/android-docker-images: Docker images used in red_mad_robot Android team
Docker images used in red_mad_robot Android team. Contribute to RedMadRobot/android-docker-images development by creating an account on GitHub.
🔥18👍9
А вы знали, что в IDEA (ну и в Android Studio) можно поменять иконку проекта?
Я не знал, а это возможно. Надо всего лишь... Нет, сода ни при чём. Надо на welcome-скрине нажать "Change Project Icon..." и выбрать иконку. Другой вариант — положить файл с названием
Интересно, что возможность такая есть уже давно. Я пытался найти когда она появилась, но нашёл только косвенные признаки, что в IDEA 2021 она уже была.
Всё, мне некогда, я пошёл ставить иконки всем проектам.
#idea
Я не знал, а это возможно. Надо всего лишь... Нет, сода ни при чём. Надо на welcome-скрине нажать "Change Project Icon..." и выбрать иконку. Другой вариант — положить файл с названием
icon.noscript или icon.png в папку .idea и ваш проект будет отличаться в списке проектов от всех остальных.Интересно, что возможность такая есть уже давно. Я пытался найти когда она появилась, но нашёл только косвенные признаки, что в IDEA 2021 она уже была.
Всё, мне некогда, я пошёл ставить иконки всем проектам.
#idea
👍8😁4🔥2
Forwarded from red_mad_dev
Какую анимацию выбрать: Composable или Suspend? Возможна ли анимация за ноль рекомпозиций? А что будет, если «обмануть» Compose и поставить @ Immutable на мутабельное значение? Об этом и многом другом рассказал Android-разработчик red_mad_robot Серёжа Чумиков в своём докладе.
Запись выступления можно посмотреть на нашем ютуб-канале, а презентацию скачать с гугл-драйва. Если пропустил первую часть доклада, читай этот пост.
#android #compose
Запись выступления можно посмотреть на нашем ютуб-канале, а презентацию скачать с гугл-драйва. Если пропустил первую часть доклада, читай этот пост.
#android #compose
👍11
Тут давно ничего не было, но для этого есть причина. Последние 3 месяца я готовился к большому автомобильному путешествию от Москвы до Черногории, а сейчас, собственно, в этом путешествии нахожусь.
На "запиливание контента" сил пока нет, но я решил прервать молчание не чтобы пожаловаться, а чтобы сделать, как я это умею, супер-запоздалый анонс :)
Сегодня в 19:00 по МСК буду участвовать в стриме на Android Guards, приходите посмотреть. Я пока не знаю что надо будет делать, но тем интереснее.
UPD: Появилась запись. Было интересно посмотреть со стороны хакеров как можно эксплуатировать разные уязвимости, в том числе те которые на первый взгляд кажутся незначительными.
На "запиливание контента" сил пока нет, но я решил прервать молчание не чтобы пожаловаться, а чтобы сделать, как я это умею, супер-запоздалый анонс :)
Сегодня в 19:00 по МСК буду участвовать в стриме на Android Guards, приходите посмотреть. Я пока не знаю что надо будет делать, но тем интереснее.
UPD: Появилась запись. Было интересно посмотреть со стороны хакеров как можно эксплуатировать разные уязвимости, в том числе те которые на первый взгляд кажутся незначительными.
Telegram
Android Guards
⚡️Внезапная, общеобразовательная активность на канале!
Завтра (27.07), в 19:00 (MSK) проведем стрим про уязвимости в Android приложениях.
Цель - покрыть вот эти топики:
- какие вообще бывают уязвимости
- что можно считать валидной уязвимостью, а что нет…
Завтра (27.07), в 19:00 (MSK) проведем стрим про уязвимости в Android приложениях.
Цель - покрыть вот эти топики:
- какие вообще бывают уязвимости
- что можно считать валидной уязвимостью, а что нет…
⚡4👍2🔥2
Как-то пропустил, что в IDEA появилась поддержка GitLab, аналогичная той что была для GitHub. Можно смотреть список MRов, ревьюить код и вливать их прямо из IDE, но есть нюанс.
Работает это пока что только для GitLab EE 15.10+. Поддержку более старых версий GitLab, а заодно Community Edition, обещают добавить в следующем bugfix релизе (issue: IDEA-326663).
JetBrains и GitLab разрабатывают плагин совместно. Поставить его можно начиная с IDEA 2023.2, так что даже канареечная студия пока что в пролёте.
#idea #git
Работает это пока что только для GitLab EE 15.10+. Поддержку более старых версий GitLab, а заодно Community Edition, обещают добавить в следующем bugfix релизе (issue: IDEA-326663).
JetBrains и GitLab разрабатывают плагин совместно. Поставить его можно начиная с IDEA 2023.2, так что даже канареечная студия пока что в пролёте.
#idea #git
The JetBrains Blog
GitLab Support in JetBrains IDEs | The JetBrains Blog
GitLab is one of the most popular git-based platforms for software development and deployment. While all basic git operations have been possible with GitLab for a long time already, integration with G
🔥4👍1
Перед очередным постом внезапный интерактив.
Сверху можно посмотреть код на Compose, который рисует красный квадрат со скошенными углами, повёрнутый на 45 градусов. Вокруг квадрата ещё рисуется синяя линяя (у неё углы не скошены).
Снизу вопрос👇 Чтобы на него ответить можно использовать IDE с превью, исходники Compose, эмулятор и вообще что угодно.
Сверху можно посмотреть код на Compose, который рисует красный квадрат со скошенными углами, повёрнутый на 45 градусов. Вокруг квадрата ещё рисуется синяя линяя (у неё углы не скошены).
Снизу вопрос👇 Чтобы на него ответить можно использовать IDE с превью, исходники Compose, эмулятор и вообще что угодно.
❤3
Будет ли отличаться результат (UI) в левом (clip + background) и правом (background) варианте кода?
Final Results
8%
Не будет, это одно и то же
55%
Будет
24%
Зависит от контента, который положим внутрь Box
13%
Напишу в комментариях / не участвую в опросе
Что ж. Прошло достаточно времени чтобы все проголосовали (и забыли про это), можно и правильный ответ сказать.
Если исходить из доки и из того как это должно работать, правильным будет 3-й вариант ответа потому что
Недавно сверстал вьюху в которой как раз использовал
С такими странными багами всегда не понятно в какую сторону копать. Интересно было бы написать пост про решение подобных проблем, подумал я, и даже придумал перед постом сделать голосовалку. Для интриги. А потом бац! И через неделю (наивный) правильный ответ с объяснениями написать.
А потом я набросал план поста и понял, что получается как-то обширно, и совершенно не понятно как это оформить в пост. Ведь хочется добавить картинок, кусков кода, и вообще как-будто больше похоже на статью… но если я уйду пилить статью, это ещё на несколько месяцев затянется. К тому же для Хабра как-будто недостаточно хардкорно. Может на Медиуме страничку завести? Или поднять себе блог на Jekyll или Hugo? Хм, какой движок для блога лучше? А стоит ли оно того? Интересна ли вообще кому-то эта тема? (ну вы поняли чем я занимался этот месяц). Чем дольше мусолю тему в голове, тем менее интересной она мне кажется и меньше хочется что-то про неё писать, так что решил разбить “пост” на логические куски и выкладывать их сюда по готовности.
Да простят меня все, кто любит смотреть сериал целиком.
План простой:
👉 Воспроизвести и понять причину
👉 Раскопать код Compose
👉 Найти workaround или фикс
#bug #compose
Если исходить из доки и из того как это должно работать, правильным будет 3-й вариант ответа потому что
clip будет обрезать не только фон самого Box’а, но и контент внутри него, а shape переданный в Modifier.background влияет только на фон. На практике же всё немного сложнее.Недавно сверстал вьюху в которой как раз использовал
rotate и clip вместе. Сверстал, проверил, что всё работает и забыл, а через пару недель мне прилетает баг, что она выглядит мягко говоря странно (см. скриншот. Я не уверен, что могу публиковать скриншоты из реального приложения, поэтому вместо красивой вёрстки тут будет некрасивый красный квадрат с обрезанными углами. Но сути это не меняет и для объяснения бага достаточно).С такими странными багами всегда не понятно в какую сторону копать. Интересно было бы написать пост про решение подобных проблем, подумал я, и даже придумал перед постом сделать голосовалку. Для интриги. А потом бац! И через неделю (наивный) правильный ответ с объяснениями написать.
А потом я набросал план поста и понял, что получается как-то обширно, и совершенно не понятно как это оформить в пост. Ведь хочется добавить картинок, кусков кода, и вообще как-будто больше похоже на статью… но если я уйду пилить статью, это ещё на несколько месяцев затянется. К тому же для Хабра как-будто недостаточно хардкорно. Может на Медиуме страничку завести? Или поднять себе блог на Jekyll или Hugo? Хм, какой движок для блога лучше? А стоит ли оно того? Интересна ли вообще кому-то эта тема? (ну вы поняли чем я занимался этот месяц). Чем дольше мусолю тему в голове, тем менее интересной она мне кажется и меньше хочется что-то про неё писать, так что решил разбить “пост” на логические куски и выкладывать их сюда по готовности.
Да простят меня все, кто любит смотреть сериал целиком.
План простой:
👉 Воспроизвести и понять причину
👉 Раскопать код Compose
👉 Найти workaround или фикс
#bug #compose
👍7🔥2
1️⃣ Воспроизвести и понять причину 🔎
Навигация: [🔼 🔽]
Да-да, я украл стиль оформления постов у @android_easy_notes. Кстати классный канал, советую
👇👇👇
Навигация: [🔼 🔽]
Да-да, я украл стиль оформления постов у @android_easy_notes. Кстати классный канал, советую
👇👇👇
👍5
🧪 Условия воспроизведения
Прежде чем фиксить нужно понять, а есть ли проблема. У меня баг не воспроизводился, поэтому запросил информацию об окружении — какое устройство, какая версия Android. Устройство оказалось ни при чём, потому что это был эмулятор, а вот на API 27, на котором обнаружили баг, он воспроизвёлся.
Но насколько всё серьёзно? Проблема только на API 27 или на других версиях тоже? Чтобы определить границы затронутых версий можно использовать бинарный поиск. Знаем, что на API 27 воспроизводится, а на 33 - нет. Представляем 27..33 в виде массива чисел. Берём версию посередине - 30, проверяем на ней. Не воспроизвелось — ищем в левой половине, воспроизвелось — в правой и повторяем пока не найдем последнюю версию в которой баг воспроизводится. Искать стоит не только вправо от API 27 (в более новых версиях), но и влево, т.к. есть шанс, что на более старых версиях баг не воспроизводился.
Применив бинарный поиск в обе стороны, я выяснил, что баг затрагивает API 24-27.
💡 Возможно это известный баг?
Раз баг есть только на некоторых версиях Android, а у нас не самая последняя версия Compose, может его уже нашли и исправили?
Это было бы самым простым решением, но к сожалению ни поиск по Issue tracker’у, ни обновление на последний Compose ничего не дали. Значит придётся самим разбираться.
⚙️ Минимальный пример воспроизведения
Чтобы точнее локализовать проблему, нужно избавиться ото всего лишнего, что не влияет на воспроизведение бага.
В моём случае одна Composable-функция была вложена в другую функцию, это всё было внутри дизайн системы в другом репозитории, а на уровне проекта обёрнуто ещё в несколько Composable-функций. Причём
Cкопировал внутреннюю Composable-функцию, к которой применяется
Найден минимальный набор модификаторов, приводящих к багу, осталось подобрать минимальный набор параметров для них. C
Попробовал подставить
Прежде чем фиксить нужно понять, а есть ли проблема. У меня баг не воспроизводился, поэтому запросил информацию об окружении — какое устройство, какая версия Android. Устройство оказалось ни при чём, потому что это был эмулятор, а вот на API 27, на котором обнаружили баг, он воспроизвёлся.
Но насколько всё серьёзно? Проблема только на API 27 или на других версиях тоже? Чтобы определить границы затронутых версий можно использовать бинарный поиск. Знаем, что на API 27 воспроизводится, а на 33 - нет. Представляем 27..33 в виде массива чисел. Берём версию посередине - 30, проверяем на ней. Не воспроизвелось — ищем в левой половине, воспроизвелось — в правой и повторяем пока не найдем последнюю версию в которой баг воспроизводится. Искать стоит не только вправо от API 27 (в более новых версиях), но и влево, т.к. есть шанс, что на более старых версиях баг не воспроизводился.
Применив бинарный поиск в обе стороны, я выяснил, что баг затрагивает API 24-27.
💡 Возможно это известный баг?
Раз баг есть только на некоторых версиях Android, а у нас не самая последняя версия Compose, может его уже нашли и исправили?
Это было бы самым простым решением, но к сожалению ни поиск по Issue tracker’у, ни обновление на последний Compose ничего не дали. Значит придётся самим разбираться.
⚙️ Минимальный пример воспроизведения
Чтобы точнее локализовать проблему, нужно избавиться ото всего лишнего, что не влияет на воспроизведение бага.
В моём случае одна Composable-функция была вложена в другую функцию, это всё было внутри дизайн системы в другом репозитории, а на уровне проекта обёрнуто ещё в несколько Composable-функций. Причём
clip применялся ко внутренней вьюхе, а rotate к одной из обёрток. Слишком много потенциальных мест где может быть ошибка.Cкопировал внутреннюю Composable-функцию, к которой применяется
clip, и добавил к ней rotate. Убрал лишние модификаторы. После каждого изменения проверял сохраняется ли баг. В итоге получил тот самый Box с несколькими модификаторами, код которого был в вопросе.Найден минимальный набор модификаторов, приводящих к багу, осталось подобрать минимальный набор параметров для них. C
background ничего особенно не придумать, а вот внутри clip у нас использовалась сложная самописная форма, которая имитирует squircle “чтоб было как в iOS“. Раз ничего нагуглить по багу не удалось, может проблема воспроизводится только у нас из-за нашей формы?Попробовал подставить
RoundedCornerShape - баг перестал воспроизводиться! Похоже и правда что-то не то с реализацией squircle, попробую максимально упростить форму, чтобы проверить догадку. Вместо сложной формы нарисовал просто треугольник и… баг воспроизвёлся. Выходит форма ни при чём, но остаётся вопрос почему тогда с RoundedCornerShape всё в порядке? Пришло время залезть в исходники Compose.🔥13
2️⃣ Раскопать код Compose 🧭
Навигация: [🔼 🔽]
Читать в телеге разбор кода с иллюстрациями такое себе удовольствие, это нужно постоянно прыгать от картинок к тексту и наоборот. Чтобы хоть чуть-чуть облегчить эту задачу:
1. Добавил цифры от 1️⃣ до 7️⃣ на картинки и в текст, чтобы было понятно какую иллюстрацию смотреть и можно было проще найти место в тексте на котором остановился. Цифры поставлены в то место в тексте, из которого по моему мнению логично прыгнуть в картинку, а потом вернуться.
2. Постарался в тексте кратко описать всё что происходит на картинках, чтобы и без них было всё понятно. Но тут вам судить насколько это получилось.
Но чтобы читать было максимально комфортно, придётся два устройства использовать. На одном картинки, на другом текст 😅
👇👇👇
Навигация: [🔼 🔽]
Читать в телеге разбор кода с иллюстрациями такое себе удовольствие, это нужно постоянно прыгать от картинок к тексту и наоборот. Чтобы хоть чуть-чуть облегчить эту задачу:
1. Добавил цифры от 1️⃣ до 7️⃣ на картинки и в текст, чтобы было понятно какую иллюстрацию смотреть и можно было проще найти место в тексте на котором остановился. Цифры поставлены в то место в тексте, из которого по моему мнению логично прыгнуть в картинку, а потом вернуться.
2. Постарался в тексте кратко описать всё что происходит на картинках, чтобы и без них было всё понятно. Но тут вам судить насколько это получилось.
Но чтобы читать было максимально комфортно, придётся два устройства использовать. На одном картинки, на другом текст 😅
👇👇👇
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6