Мобильное Чтиво
1.6K subscribers
276 photos
45 videos
159 links
Очень серьезный канал про мобильную разработку. Веду канал я — @maxkachinkin
Download Telegram
This media is not supported in your browser
VIEW IN TELEGRAM
🎨 Главное — чтобы было красиво!

Тесты, шместы, архитектура — это всё прекрасно. Но в итоге главное — чтобы было красиво! Я вот вспомнил одну нашу фичу, где надо было сделать кастомный контрол типа табов, которые плавно анимировались, центрировались на выбранном, а потом обратно схлопывались. Всё на Compose, конечно. 💻

И что вы думаете? Контентные паддинги в LazyRow не помогли, игры с отступами тоже. Даже использование horizontalScroll не дало результата. Пришлось думать дальше. 🤔

А как в итоге сделали? Ну, это можно назвать костылём (или нормальным решением, если вам так больше нравится). Добавили "фейковые" элементы в начале и в конце списка и анимировали их размер. 🙃

Используем LazyRow и делаем первый и последний item просто прозрачные Spacer, чтобы создать видимость отступов. Плавно и красиво анимируем их ширину, и всё! 💫 На самом деле не совсем всё: это тянет за собой много всего, чтобы учитывать эти элементы по-особенному (чтобы не кликались, не анимировались, не участвовали в выборе и т.д.).

Как заметили в комментариях, это создает дополнительные рекомпозиции 🫣, что не может радовать. Такой трейдофф решили взять. Но в итоге всё выглядит плавно, аккуратно, ну и просто красиво! 🌟

В комментах я добавлю скриншот кода и видосик — там видно, как это анимируется и центрируется. 🎥

А у вас какие были проблемы из-за красоты? Поделитесь! 😎

#android #compose #ui #lazyrow
👍15💯4🥰1
⚙️ Kotlin in GitHub Actions ⚙️

Совсем скоро Мобиус 2024 Autumn, и я там выступаю с докладом Kotlin in GitHub Actions. Расширяем горизонты KMP. 😎

Я расскажу, как написать свой кастомный GitHub Action на Kotlin/JS: что для этого нужно, как всё работает и какие подводные камни могут встретиться.

Если вы никогда не писали кастомный GitHub Action на Kotlin/JS, но вдруг захотели до моего доклада, то, уверен, потратите прилично времени, чтобы разобраться. 🕵️‍♂️

А после моего выступления вы точно сделаете это намного быстрее! 🧠

А вы писали когда-нибудь на Kotlin/JS?

#android #kmp #kotlinjs
🔥11👍3
🤩 Билет на Mobius! 🤩

Я разыгрываю билет на Mobius 2024 Autumn!

Условия участия просты: написать сообщение под этим постом, какой доклад вам больше всего хочется послушать!

Чтобы выиграть не обязательно писать, что мой доклад Kotlin in GitHub Actions самый интересный 😅 Победит чистый рандом! 🎰

Билет может быть как онлайн, так и оффлайн, как вы захотите.

Итоги подвожу в четверг 10 октября! 📅

#mobius
🔥9👍4😁1
“Смотрим” на Compose через <clinit>

Я хочу вам рассказать про 2 метода: <init> и <clinit>. 👇

<init> — это метод-конструктор. Каждый раз, когда мы создаем новый объект через new, JVM вызывает этот метод, чтобы инициализировать объект и его поля. 📦

<clinit> — это статический инициализатор класса. Этот метод вызывается, когда класс впервые загружается в память. И да, это происходит только один раз при первой загрузке класса. 🔄

Когда и кем вызывается?

JVM автоматически вызывает <clinit> при первой загрузке класса в память.
А вот <init> вызывается каждый раз, когда вы создаете новый экземпляр класса.

Теперь к интересному! 💡

Смотрите на картинку. Здесь показан запуск Compose-экрана, когда он открывается впервые в приложении, и до этого никакие Compose-функции не были вызваны.

На диаграмме чётко видно, как активно срабатывают <clinit> методы. Это часть подсвечена. Это как раз и есть свидетельство того, что Compose — это unbundled library. Я как-то рассказывал про это на Mobius, но эта картинка показывает это наглядно.

А вот при повторном запуске? Никаких <clinit> уже нет. 🎯

Да, конечно, тут не только классы Compose, но если порыться в методах, то большинство из них типо такие:
androidx.compose.foundation.layout.RowKt.<clinit>
androidx.compose.foundation.layout.ColumnKt.<clinit>


Вывод: При первой загрузке Compose через класс-лоадер загружается больше количество классов. 🚀

P.S. Не забывайте, в закрепе розыгрыш БЕСПЛАТНОГО билета на Mobius! 🎁

#android #compose #kotlin #jvm
👍8🔥6
📜 Ваш любимый логгер! 📜

Мы в своих Android-проектах давно логируем через SLF4J/Logback Android. Да, да, это тот самый проверенный временем логгер.

🎯 У него есть плюсы: Надежный (не считая истории с CVE-2023-6481, но там речь про классический, а не android), стабильный, с хорошей поддержкой форматов логирования. Работает без сюрпризов, настройки для форматирования и фильтрации логов. В общем по функционалу всё нравится.
Но есть главный минус: Увы, только для Android. Не подходит для KMP, да и по гибкости проигрывает более свежим решениям.

Я понимаю, что SLF4J/Logback — это не самый гибкий и современный подход, особенно если ваши проекты уже идут в стороны Kotlin Multiplatform.

А как у вас с логированием дела обстоят? 😎

- Logback Android, как у нас?
- Может быть Timber?
- Или уже думаете о будущем и логируете с помощью 🦸‍♂️Napier или 🐸Kermit?
- Или, может, у вас есть свой подход?

Поделитесь, очень интересно узнать! 👀

P.S. Не забывай! В закрепе БЕСПЛАТНЫЙ билет на 🟢Mobius🟢! 🎁 Успейте до четверга!

#android #kmp #logging
5🤓1
🔥 Топ ожидаемых докладов 🔥

Недавно я запустил конкурс на бесплатный билет на Mobius (у вас еще остался 1 день, завтра днем крутану рандомайзер 🎰), и я попросил написать, какие доклады вы ждете больше всего. Кто-то написал 1 доклад, кто-то несколько. Я учел и посчитал все упоминания.

🎤 И вот самые ожидаемые доклады по версии подписчиков Мобильное чтиво:

Первое место разделили:

🥇 Не два байта переслать: эмуляция бесконтактных карт на мобильных устройствах
Павел Васильев, Positive Technologies

🥇Что не так с мобильными сервисами в Android и iOS
Кирилл Розов, Android Broadcast

🥇Kotlin in GitHub Actions. Расширяем горизонты KMP
Максим Качинкин, Dodo Engineering
Эти доклады набрали по 5 голосов.

Могу предположить, что мой доклад упоминали, потому что я веду этот канал 😏. Поэтому вот доклады, которые на 2м месте — они набрали по 4 голоса. Тоже очень ожидаемые!

Второе место:

🥈 Compose и SwiftUI: найди 10 отличий
Алексей Панов, Контур

🥈 Опасности в Android: уязвимости и защитные меры
Юлия Стекачева, Райффайзен Банк

🥈 Предпринимательство для инженера: как запустить свою компанию
Евгений Мацюк, MarathonLabs

🥈 Суперапп с чистого листа
Сергей Балалаев, Ozon

Все вышеперечисленные доклады я тоже хочу посмотреть. Но я еще добавлю от себя, что мне интересно.

Я жду вот эти 2:

🚀 Заезжаем в KMP. Но какой ценой?
Денис Александров, Яндекс 360

🚀 Последнее слово в Android-навигации
Данил Колесников, Дзен

#Mobius2024 #Android
5👍2🔥2
This media is not supported in your browser
VIEW IN TELEGRAM
🔥15👍1😁1
🤣 Пятничный мем 🤣

Сегодня выступил на Mobius на online дне. Про само выступление напишу потом. Сегодня просто легкий пятничный пост.

Как я готовился к выступлению с визуальной части. Есть 3 момента:

- Мне нравится рассказывать стоя. Так я чувствую себя более динамично и бодро. 💪
- Хочется, чтобы фон был красивый. 🎨
- У меня нет стола с регулировкой высоты, поэтому приходится что-то придумывать. 🤔

Для этого я нашел стену, обустроил её как надо (бахнул на телек красивую картинку), а чтобы встать рядом с ней стоя, пришлось соорудить конструкцию из того, что попалось под руку (включая коробку из-под обуви). И, конечно, надо поставить кружку воды, мобилу и прочее. 📱☕️

И получился мем:
Frontend - Backend 😄

#mobius #meme #пятница
😁29🔥6👍4🤣41
📅 Как проходит мой рабочий день 🏃‍♂️

Вышла статья из рубрики “День с экспертом” на SkillFactory Media. Там я рассказываю про свой типичный рабочий день.

Из интересного и про мобильную разработку я рассказываю, что:
• у нас есть правило на короткие PRы 📝
• мы работаем по Trunk-Based Development 🌳
• я люблю парное программирование, но удается применять его редко 👨‍💻👩‍💻
• подставка под ноут — это маст 📦
• каждый день кормлю уличных котов 🙂

Заголовок и тон статьи может вызвать подозрение на тупые понты или что я собрался продавать мастер-классы “Эффективная эффективность: успевайте всё как я”. Знайте, такого намерения не было. 😅

💬 Мне интересно, а что у вас любимое в вашем рабочем дне? Или наоборот, что вас бесит?
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥2🍌2😁1🙈1
🇦🇲 Увидимся на DevFest в Ереване 🇦🇲

В это воскресенье, 20 октября, в Ереване пройдет DevFest Armenia 2024!

Я буду выступать там (да, оффлайн) с докладом:
Compose as an Unbundled Library: What It Means for Your App.

Подписчики этого канала знают, что на эту тему я делал посты здесь и ни раз. Вот теперь расскажу целиком вслух:
🐣 Что значит unbundled library
📏 Как замерять UI перфоманс
⏱️ Как можно оптимизировать Compose, если все-таки столкнулись с долгой первой загрузкой

Если вдруг кто есть в Ереване, го на DevFest, пообщаемся!

#devfest #yerevan #compose
🔥122👍1
🎨 Зато Compose не скован версиями Android 🤖

Я пару раз уже писал на эту тему Compose как Unbundled Library. Сейчас напишу в последний раз (надеюсь 😅). Но теперь с конкретикой и расскажу, чем это хорошо или плохо.

В отличие от Compose, все классы View уже лежат в памяти нашего процесса еще до того, как вы вообще успели запустить приложение!

Когда же они туда попали? 🧠

Ответ — еще до старта вашего приложения! При инициализации процесса Zygote. 🧬

Есть такой файл, он называется preloaded-classes и лежит вот тут:
/system/etc/preloaded-classes.

В этом классе строчка за строчкой записаны все полные имена классов Android фреймворка. Их там больше 17k! Включая, конечно же, классы системы View! 📜

Потом в методе preloadClasses мы читаем этот файл, строчку за строчкой, и загружаем классы в память процесса!

Вот так! Еще до ващего Application::onCreate, до контент провайдеров, до того как ваш процесс форкнется от Zygote, в нем уже всё будет загружено!

У Compose такой магии нет. Но отсутствие предзагрузки позволяет нам подключать разные версии Compose и обновлять его гибко.

Поэтому Compose не скован никакими версиями Android фреймворка. Мы не пишем с вами код, типа:
VERSION.SDK_INT >= VERSION_CODES.TIRAMISU

для компоуз кода.

Отвечая на вопрос чем это плохо 👎:
- Compose требует времени на загрузку в рантайме
Чем это хорошо 👍:
- Наконец-то есть гибкость и свобода!

💬 Вам нравится гибкость? Или вы бы предпочли предзагрузку вместе с Android фреймворком?

Кстати, на фото — это как раз я на DevFest Armenia на этих выходных, объясняю вот этот самый момент про Zygote и preloaded классы. 📸

#Compose #DevFest
👍27🔥153
This media is not supported in your browser
VIEW IN TELEGRAM
🚀 Как лучше работать с OTP?

Чтобы автоматически обрабатывать SMS, мы в Drinkit используем SMS Retriever API от Google.

Это популярный инструмент для такой задачи. Кто немного не знаком с ним я перечислю основные моменты:

1️⃣ Не нужны разрешения. Чтобы использовать это API, не нужно запрашивать у пользователя доступ к SMS. SmsRetriever даёт нам 5-минутное окно, в течение которого система ожидает входящее сообщение с кодом.

2️⃣ Безопасность. SMS Retriever API ожидает хеш приложения, который зашивается в SMS. Это значит, что у нашего приложения будет доступ только к “нашим” SMS-кам.

3️⃣ Удобство для пользователя. Если всё сделать, то код подтягивается автоматически, и пользователю не нужно вручную копировать его из сообщения. 👌 Прямо как на видео, смотрите!

Конечно, это не единственный способ. Есть и другие варианты:

- Использовать One-tap SMS verification API.
Там будет дополнительный Bottom Sheet с подтвеждением. Но, если честно, я такой встречаю крайне редко.
- Вообще не автоматизировать — пользователь сам вводит код. Периодически я встречаю такие приложения.
- BroadcastReceiver или Content Provider — старые подходы, которые требуют больше пермишенов и возни с кодом.

Однако, я заметил, что не все приложения используют SMS Retriever API. Например, видел что им пользуются Uber или Яндекс Такси. Но далеко не все приложения. 🤔 Интересно, почему?

А вы что используете для обработки OTP у себя? Делаете через SMS Retriever API или выбираете другой путь? Стоит ли мне подробнее расписть про SMS Retriever API? Делитесь в комментах! 😎

P.S. Мартышки, это не часть нашего UI, просто я скрыл номер телефона ☺️

#android #smsretriever #otp
👍24🤔21
🎃 Чего я боюсь 🎃

В Хеллоуин принято пугать друг друга, в шутку и не очень. Давайте поговорим о том, что нас пугает в разработке!

Вот мой топ 3 самых больших страха!

1️⃣ Случайно нажать Sync Projects With Gradle Files вместо нужной кнопки... и просто замереть в ужасе. 🧙‍♂️

2️⃣ Когда ты iOS-разработчик, а тебе говорят: "Сделай как на Android." 🧛‍♂️

3️⃣ Идешь читать доку "Migrate apps to Android 13/14/15" (подставь любую версию) 🕸️

Напишите в комментах, а какие у вас страхи? Если не боитесь! 👻
😁16👍2
This media is not supported in your browser
VIEW IN TELEGRAM
🇺🇸 Рисуем SVG в Compose Great Again

Сегодня расскажу, как можно нарисовать сложный рисунок в Compose, если у вас есть SVG файл. А именно как распарсить SVG файл в удобные для нас Path и отрисовать их “руками”.

🔍 Парсинг SVG: Мы загружаем SVG файл с помощью XmlPullParser, который читает каждый элемент. В каждом path элементе хранится информация о том, как рисовать фигуру — всё закодировано в атрибуте d. Этот атрибут содержит команды типа "M" (move to), "L" (line to), "C" (curve to) и координаты. Вместе они формируют контур, который и станет нашим рисунком.

🛠️ Конвертация Path: Здесь проблема. В Compose пока нет метода для декодирования пути напрямую из d атрибута SVG.

Но мы сделаем конвертацию Great Again! Мы сначала создаём старый добрый Path из androidx.core.graphics (через метод createPathFromPathData), а потом конвертируем его в Compose Path с помощью asComposePath. Немного костыльно, но работает.

Теперь, когда мы всё распарсили и конвертировали, можем просто отобразить это в Compose.

Я выложил пример на GitHub, где вы можете посмотреть, как это работает. В этом проекте пярмо то, что изображено на видосе, какая-то карта и какие-то непонятные области.

P.S. Да, нажатия сделаны не совсем идеально — попадание внутрь Path осуществляется по bounding box, а не точно по форме. Но это уже отдельная задача, а здесь у нас фокус на отрисовке! 😎

💬 А что интересного вы рисовали в Compose?

#compose #svg
🔥21😁7👍3
🚀 Используй ScatterMap, если ты помешан на производительности

Обожаю Romain Guy и его выступления. Недавно смотрел его видео Exploring Kotlin Performance на канале Code with the Italians 🎥

Он рассказывает, как писать код на Kotlin так, чтобы он был максимально производительным. Показывает во что компилируется Kotlin и анализирует это.

Мы можете мне возразить: зачем мне это? Ведь моё дело написать читаемый код, а дело разработчиков компиляторов сделать так, чтобы он работал быстро!

Но. Если вы хотите написать супер производительный UI и особенно на Compose, то тут каждая мелочь может иметь значение!

Я приведу один пример из видео: Map vs ScatterMap.

Если вы используете HashMap, то в скомпилированном коде будет много вызовов функций и аллокаций. И даже если GC у нас больше не в мейн треде, то лишние аллокации все равно не желательны для супер производительного UI. 🧹

Так вот, в таких случаях можно использовать ScatterMap. Эта штука ведет себя почти как Map, но работает с ключами по-другому и требует гораздо меньше аллокаций. Это значит, что производительность становится выше, особенно в UI-интенсивных задачах.

Но стоит учитывать пару нюансов:
- ScatterMap не потокобезопасен 🕵️ (просто держите это в уме)
- он не очень эффективен, если вы часто удаляете элементы или работаете с огромным количеством данных.

💬А вы когда-нибудь заморачивались, чтобы использовать ScatterMap? 🤔
Разобрать ли мне более подробно ScatterMap?

#ui #performance #scattermap
👍14🔥41
Как разрулить рисование пальцем и Zoom в Compose?

Мой коллега, Дима, очень любит Compose и пишет про него всякие интересные посты, кстати, подписывайтесь на него.

Например, в этом посте он разбирает как сделать так:
- если вы дотронулись одним пальцем, то вы рисуете;
- а если двумя — то зумите/поворачиваете.

Дима написал модифайер, там есть Gist. Если столкнетесь с подобной задачей, то будете знать, как её решать!

#compose #canvas
👍7
This media is not supported in your browser
VIEW IN TELEGRAM
В продолжение поста про рисование на Canvas, у нас появляется новое требование 🚨
Что если нам надо теперь уметь трансформировать холст?

Есть конечно идея сделать режим отдельной кнопкой, но будет не интуитивно. Мы постараемся сделать это бесшовно, как это часто делают хорошие фоторедакторы 🖼

Наш главный кандидат – PointerInputScope.detectTransformGestures(). Он сразу посчитает все значения и отдаст нам pan, zoom, rotation

Однако сразу как добавим его, обнаружим, что он конфликтует с жестом рисования detectDragGestures(). Причина простая – жест обрабатывается самым вышестоящим обработчиком и не пробрасывается дальше

Modifier
.pointerInput(Unit) {
detectDragGestures()
}
.pointerInput(Unit) {
detectTransformGestures()
}

Чтобы избежать этого, нужно что-то творческое

Возьмем исходный код из detectTransformGestures и подкорректируем его с учетом пожеланий:

1. Когда жест происходит одним пальцем, мы рисуем. Значит нам дополнительно нужно отслеживать drag, примерно как делает detectDragGestures
2. Когда жест двумя пальцами, начинаем трансформирование. И рисовать уже не получится
3. Когда мы начинаем одиночный жест, и с задержкой ставим второй палец, режим переходит в трансформацию только в случае если не преодолели threshold по drag. А нарисованное удаляем

В итоге получаем обработчик жеста – опубликовал его в виде Gist
Отличия от стандартной реализации – более сложное условие для начала трансформации

var dragAmount = 0f  
val dragAmountThreshold = 50f

// 2 pointers is required for transform gesture
val gesturePointersRequirementMet = downPointerCount > 1

if (!canceled && gesturePointersRequirementMet && dragAmountThreshold > dragAmount) {
// Handle gesture


И в если не проходим по условию – просто рисуем на канве
else {  
val change = event.changes.first()
if (!dragGestureStarted) {
dragGestureStarted = true
onDragStart(change.position)
}
if (transformGestureStarted) return@awaitEachGesture
onDrag(change.positionChange())
dragAmount += change.positionChange().getDistance()
}


Не забудем трансформирование применить в Modifier.graphicsLayer{} и динамический режим рисования/трансформирования как на видео готов!

Полный пример я опубликовал в репозитории про Canvas из прошлого поста

В процессе реализации я дополнительно опирался на репо с расширенными жестами. Возможно, если вы ищете что-то нестандартное, это может быть уже реализовано 😋

#compose #gestures #modifier #yandexcup
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥6
📺 Про ScatterMap на видео 📽️

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

Когда начал готовить пост, то понял, что материала много, и я в пост не уложусь, даже если буду рассказывать чисто по верхам. Это надо статью писать. Но статью писать (пока) не хочу, и я решил попробовать рассказать кратко в форме видоса. Экспериментальный формат 😅

Цель видоса — рассказать кратко суть и логику ScatterMap. Чтобы “на пальцах” было понятно что к чему. Не вдаваясь в детали. А те кто хочет — вэлкам читать статьи и слушать часовые доклады.

Например, кто заинтересовался, можно посмотреть эти материалы:
- Статья от Romain Guy про ScatterMap
- Видос Designing a Fast, Efficient, Cache-friendly Hash Table, Step by Step от Matt Kulukundis, где он очень классно рассказывает логику работы такой мапы

Получилось, что получилось. Но вы мне скажите, как вам? (Видос будет следующим постом)
🔥7
Media is too big
VIEW IN TELEGRAM
Как работает ScatterMap
🔥22👍101