• Адаптивный UI. Темы, динамические цвета, вырезы, складные устройства, большие экраны, планшеты, десктопы, часы и гарнитуры. Все это нет смысла поддерживать в начале разработки. Хотя гугл проделывает огромную работу, чтобы адаптивность работала из коробки без усилий с нашей стороны. Чем дальше, тем легче ее поддерживать.
• Доступность. Talkback, высококонтрастный текст, инверсия цветов, contentDescription, fontScale и порядок фокуса. Все это добавляется позднее.
• Тесты. Написание полноценных тестов может занять столько же времени, сколько и сама фича. Проверить функциональность можно вручную.
Как итог – кабаныч доволен темпами разработки. В будущем есть вероятность присесть на бенч, рефакторить и обмазываться лучшими практиками. Раньше времени не сгорел. Через год проект не полетел и закрылся – не обидно за вложенные силы. Не случился эффект безвозвратных потерь.
• Доступность. Talkback, высококонтрастный текст, инверсия цветов, contentDescription, fontScale и порядок фокуса. Все это добавляется позднее.
• Тесты. Написание полноценных тестов может занять столько же времени, сколько и сама фича. Проверить функциональность можно вручную.
Как итог – кабаныч доволен темпами разработки. В будущем есть вероятность присесть на бенч, рефакторить и обмазываться лучшими практиками. Раньше времени не сгорел. Через год проект не полетел и закрылся – не обидно за вложенные силы. Не случился эффект безвозвратных потерь.
Предпоследний отчет. За июль решена 31 задача уровня изи. Всего 339. Поднялся в рейтинге на 310 857 место. Выдали пятый почетный бейдж 200 Days Badge 2025.
• XOR Operation in an Array
• Average Salary Excluding the Minimum and Maximum Salary
• Path Crossing
• Can Make Arithmetic Progression From Sequence
• Reformat Date
• Number of Good Pairs
• Water Bottles
• Count Odd Numbers in an Interval Range
• Patients With a Condition
• Shuffle String
• Count Good Triplets
• Kth Missing Positive Number
• Make The String Great
• Three Consecutive Odds
• Thousand Separator
• Most Visited Sector in a Circular Track
• Detect Pattern of Length M Repeated K or More Times
• Matrix Diagonal Sum
• Replace All ?'s to Avoid Consecutive Repeating Characters
• Customer Who Visited but Did Not Make Any Transactions
• Special Positions in a Binary Matrix
• Bank Account Summary II
• Sum of All Odd Length Subarrays
• Rearrange Spaces Between Words
• Crawler Log Folder
• Design Parking System
• Special Array With X Elements Greater Than or Equal X
• Maximum Nesting Depth of the Parentheses
• Mean of Array After Removing Some Elements
• Largest Substring Between Two Equal Characters
• Minimum Changes To Make Alternating Binary String
#leetcode@foundout
Please open Telegram to view this post
VIEW IN TELEGRAM
Как делать то, что не хочется
В жизни полно важных дел, которые даже начинать не хочется – тренировка, статья, презентация, резюме, план поездки. Их объединяет одно – желание отложить на потом. Если откладывать уже некуда – находится любой повод для промедления: ютуб, твиттер, телеграм, рилсы, уборка, шопинг, сплетни. Но рано или поздно придется сдвинуться с мертвой точки. От себя не убежать.
Чтобы сделать дело и гулять смело, надо разобраться, что нас стопорит. Никто не появился на свет бездействовать.
Анализ будет таким:
• Почему дела избегаются
• Почему пробуется все, кроме взять и сделать
• Как мозг ранжирует дела
• Как понять, что дело важное
• Как выполнить дело
• Как выполнить следующее дело
🏃♂️ Почему дела избегаются
Мозг предпочитает быстрые награды долгосрочной пользе. Этот плавающий в бульоне орган даже в полном покое потребляет 20% всей энергии организма. В условиях дефицитного рациона такая прожорливость становится критичной. Поэтому поощряются поведенческие схемы, позволяющие экономить калории.
Древняя прошивка торгуется с эволюционно новыми лобными долями, отвечающими за самоконтроль и целеполагание. Мозг постоянно оценивает сколько энергии, внимания и эмоционального напряжения потребует предстоящая работа. Если выгода не перекрывает издержки, мотивация падает.
Мы откладываем дела, соответствующие нашим лучшим интересам, осознавая при этом негативные последствия. Боль, скука и тревожное ожидание неудачи активируют те же участки, что и сигнал физической угрозы. Возникает замкнутый цикл избегания: задача вызывает тревогу → откладывание → кратковременное облегчение → усиление тревоги.
💻 Почему пробуется все, кроме взять и сделать
Мы превращаем взять и сделать в бесконечную череду подготовительных действий. Подменяем неприятное действие приятным ощущение контроля. Химия та же, но усилий меньше.
Пока задача «в планах», она безупречна. Стоит начать, и происходит столкновение с реальностью, критикой и сомнениями. Альтернативные процессы вместо работы дают чувство занятости и спасают от страха провала: «я не ленивая жопа, я занят». Ожидание идеальных условий отдаляет момент, когда придётся выдать результат и потенциально столкнуться с его несовершенствами.
К несчастью, стресс только усиливается. Время идёт, идеальные условия не наступают, а значимость результата в голове раздувается до ужасающих масштабов. В конце концов, никакие танцы с бубном вокруг задачи не принесут того удовлетворения, которое даст её успешное завершение.
😑 Как мозг ранжирует дела
Вычислительный бюджет ограничен, поэтому мозг быстро сортирует события. Сравнивая ожидаемую награду с затратами энергии, времени и риском ошибки. Действия без эмоционального всплеска и новизны сразу уходят в фоновый шум.
Личный опыт отдельно окрашивает оценку. Если похожий эпизод прошёл без награды и опасности, дело получает ярлык «нейтрально» и отправляется в отстойник приоритетов. Реальная ценность задачи может быть высокой, но если награда отложена, эмоции слабы, новизна нулевая, а цель сформирована туманно – сигнал о важности не пройдет. Поэтому задачи «важно, но не срочно» требуют сознательной воли.
Также обесцениваются дела без ощутимой награды вообще. Рутина без явного бонуса или проекты с неопределенным результатом воспринимаются особенно скучными. Низкая вероятность, что усилия окупятся помноженная на низкую ценность приводит к решению экономить энергию. Хотя выигрыш может быть скрыт или отдален.
😊 Как понять что дело важное
Если ты подумал о нем больше пяти раз – это сигнал о важности. Повторяющиеся мысли прямо указывают на высокий приоритет.
Есть и физиологическая подсказка: важные, но трудные действия вызывают лёгкое внутреннее «упрямство» – мозг оценивает усилия и риски. Ощущение сопротивления при ясном понимании отдаленной пользы говорит о действительной значимости. Иначе спора не было бы вовсе.
Сложим ценность, последствия невыполнения и уникальность момента – получим почти инженерную схему принятия решения. Чем отчётливее каждая грань, тем выше приоритет, независимо от того, насколько задача приятна сейчас.
В жизни полно важных дел, которые даже начинать не хочется – тренировка, статья, презентация, резюме, план поездки. Их объединяет одно – желание отложить на потом. Если откладывать уже некуда – находится любой повод для промедления: ютуб, твиттер, телеграм, рилсы, уборка, шопинг, сплетни. Но рано или поздно придется сдвинуться с мертвой точки. От себя не убежать.
Чтобы сделать дело и гулять смело, надо разобраться, что нас стопорит. Никто не появился на свет бездействовать.
Анализ будет таким:
• Почему дела избегаются
• Почему пробуется все, кроме взять и сделать
• Как мозг ранжирует дела
• Как понять, что дело важное
• Как выполнить дело
• Как выполнить следующее дело
Мозг предпочитает быстрые награды долгосрочной пользе. Этот плавающий в бульоне орган даже в полном покое потребляет 20% всей энергии организма. В условиях дефицитного рациона такая прожорливость становится критичной. Поэтому поощряются поведенческие схемы, позволяющие экономить калории.
Древняя прошивка торгуется с эволюционно новыми лобными долями, отвечающими за самоконтроль и целеполагание. Мозг постоянно оценивает сколько энергии, внимания и эмоционального напряжения потребует предстоящая работа. Если выгода не перекрывает издержки, мотивация падает.
Мы откладываем дела, соответствующие нашим лучшим интересам, осознавая при этом негативные последствия. Боль, скука и тревожное ожидание неудачи активируют те же участки, что и сигнал физической угрозы. Возникает замкнутый цикл избегания: задача вызывает тревогу → откладывание → кратковременное облегчение → усиление тревоги.
Мы превращаем взять и сделать в бесконечную череду подготовительных действий. Подменяем неприятное действие приятным ощущение контроля. Химия та же, но усилий меньше.
Пока задача «в планах», она безупречна. Стоит начать, и происходит столкновение с реальностью, критикой и сомнениями. Альтернативные процессы вместо работы дают чувство занятости и спасают от страха провала: «я не ленивая жопа, я занят». Ожидание идеальных условий отдаляет момент, когда придётся выдать результат и потенциально столкнуться с его несовершенствами.
К несчастью, стресс только усиливается. Время идёт, идеальные условия не наступают, а значимость результата в голове раздувается до ужасающих масштабов. В конце концов, никакие танцы с бубном вокруг задачи не принесут того удовлетворения, которое даст её успешное завершение.
Вычислительный бюджет ограничен, поэтому мозг быстро сортирует события. Сравнивая ожидаемую награду с затратами энергии, времени и риском ошибки. Действия без эмоционального всплеска и новизны сразу уходят в фоновый шум.
Личный опыт отдельно окрашивает оценку. Если похожий эпизод прошёл без награды и опасности, дело получает ярлык «нейтрально» и отправляется в отстойник приоритетов. Реальная ценность задачи может быть высокой, но если награда отложена, эмоции слабы, новизна нулевая, а цель сформирована туманно – сигнал о важности не пройдет. Поэтому задачи «важно, но не срочно» требуют сознательной воли.
Также обесцениваются дела без ощутимой награды вообще. Рутина без явного бонуса или проекты с неопределенным результатом воспринимаются особенно скучными. Низкая вероятность, что усилия окупятся помноженная на низкую ценность приводит к решению экономить энергию. Хотя выигрыш может быть скрыт или отдален.
Если ты подумал о нем больше пяти раз – это сигнал о важности. Повторяющиеся мысли прямо указывают на высокий приоритет.
Есть и физиологическая подсказка: важные, но трудные действия вызывают лёгкое внутреннее «упрямство» – мозг оценивает усилия и риски. Ощущение сопротивления при ясном понимании отдаленной пользы говорит о действительной значимости. Иначе спора не было бы вовсе.
Сложим ценность, последствия невыполнения и уникальность момента – получим почти инженерную схему принятия решения. Чем отчётливее каждая грань, тем выше приоритет, независимо от того, насколько задача приятна сейчас.
Please open Telegram to view this post
VIEW IN TELEGRAM
Слона нужно жрать по кусочкам, потому что громадная задача парализует, а маленький шаг – нет. Договорись с собой, что поработаешь над задачей только пять минут. Разреши остановиться, если работа окажется невыносимой. Такая маленькая «сделка» снижает психологический порог. На краткое усилие легче согласиться.
Практика показывает, что, начав, мы почти никогда не бросаем дело через пять минут. Первоначальное сопротивление отступает, тревожность падает, избегание сменяется вовлеченностью. Правило пяти минут фактически обманывает мозг: ради снятия стартового мандража он соглашается потрудиться на короткое время. Через 10-15 минут непрерывной работы саморефлексия заметно приглушается и ты оказываешься в состоянии, когда время течёт незаметно, а работа «идёт сама».
На примере написания текста: едва появляется крошечный прогресс (первый абзац), мозг снижает «цену» усилия и включает лампочку «выигрыш возможен». Повышается бдительность и запускается петля самоподкрепления – рычаг, который тянет тебя дальше. Наступает состояние потока, когда задача выполняется точнее и быстрее, чем под нажимом волевой дисциплины. Мы больше не думаем об объёме работы, а концентрируемся на ближайшем кусочке. Это не эзотерика. Ничего не получится, если бесконечно собираться с силами и не начать действовать.
К сожалению, мы забываем о «пятиминутке» также, как о зонте в ливень. Паттерн жив пока держится в цепочке внимания. Контекст сменился – цепочка схлопнулась. Сознание сразу заполнилось новыми тревогами.
Также добавляется контекстная привязка. Установка живёт вместе с окружением: сидишь в любимом кресле, открыт редактор, падает знакомый угол света. Сменил комнату или посмотрел в телефон – «крючок» обрывается и мозг уже не связывает свежий контекст с прежним правилом. Я однажды передвинул стол в комнате и былая концентрация утратилась, пока не вернул его обратно.
Закрывая задачу, мы завышаем уверенность в своей дисциплине («справился сейчас – справлюсь и дальше») и недооцениваем предстоящее сопротивление. То, что правило забывается, не дискредитирует метод. Ему нужен новый крючок внимания перед каждым заходом.
…
Нежелание браться за работу – биологический расчёт, а не обвинительный приговор. Поняв логику этого расчёта, ты получаешь инструменты, чтобы убедить мозг играть на своей стороне. Применяй правило пяти минут к каждой отложенной задаче. Начатое хочется довести до конца. Аппетит приходит во время еды. А продолжать всегда легче, чем начинать.
Please open Telegram to view this post
VIEW IN TELEGRAM
Свистоперделки
Есть категория постов в технических тг-каналах, полезность которых стремится к отрицательной. Речь об ископаемых гитхаб-репозиториях с какой-нибудь юайной свистоперделкой: ленточки, стильные прогресс-бары, кнопка с прогрессом, виджеты разблокировки и прочее и прочее и прочее.
Востребованы ли свистоперделки в коммерческой разработке? Никак нет. Даром не нужны. Все как-то обходятся стандартными вьюхами, собственной библиотекой компонентов и нативными анимациями.
Можно пофантазировать, что когда-нибудь твой менеджер сойдет с ума и вместо обычной кнопки-сосиски захочет переливающийся градиентом фаербол, с вылетающими при клике конфетти. И вот, с сохраненного поста сдувается пыль, усилия, ранее затраченные на переход по ссылке, чтение ридми и менеджмент закладок, окупаются. Но, почти наверняка, такого не будет. Время потеряно безвозвратно. Внимание ушло в никуда.
Свистоперделки хороши только для заполнения звенящей пустоты контентной ленты, когда нет идей для постов. При этом подобные публикации развивают цифровой накопительный синдром и эксплуатируют человеческий страх упустить важное. Ты либо пересылаешь их себе, чтобы никогда больше не открыть, либо вырабатываешь баннерную слепоту и просто пролистываешь.
Подход к потреблению информации должен быть не реактивным (пассивное следование), а императивным (целенаправленный поиск). С постепенным развитием включается закон убывающей отдачи: информация начинает приносить меньше пользы, чем реальный опыт. Поэтому эффективнее работает контекстное обучение, а усваивается то, что сразу применяется в работе.
К тому моменту, когда свистоперделка реально пригодится, ее разработчик уйдет в запой, а репозиторий в архив. Все равно придется в моменте искать поддерживаемое решение. Работает это так: вдруг понадобился виджет календаря, заряжаем в o3 промпт следующего содержания: «найди на гитхабе виджет календаря, на compose, проект активно развивается, больше 100 звезд у репо, есть выбор интервала дат». Нейросеть плюется результатами: раз, два и три. Применяем и радуемся.
Есть категория постов в технических тг-каналах, полезность которых стремится к отрицательной. Речь об ископаемых гитхаб-репозиториях с какой-нибудь юайной свистоперделкой: ленточки, стильные прогресс-бары, кнопка с прогрессом, виджеты разблокировки и прочее и прочее и прочее.
Востребованы ли свистоперделки в коммерческой разработке? Никак нет. Даром не нужны. Все как-то обходятся стандартными вьюхами, собственной библиотекой компонентов и нативными анимациями.
Можно пофантазировать, что когда-нибудь твой менеджер сойдет с ума и вместо обычной кнопки-сосиски захочет переливающийся градиентом фаербол, с вылетающими при клике конфетти. И вот, с сохраненного поста сдувается пыль, усилия, ранее затраченные на переход по ссылке, чтение ридми и менеджмент закладок, окупаются. Но, почти наверняка, такого не будет. Время потеряно безвозвратно. Внимание ушло в никуда.
Свистоперделки хороши только для заполнения звенящей пустоты контентной ленты, когда нет идей для постов. При этом подобные публикации развивают цифровой накопительный синдром и эксплуатируют человеческий страх упустить важное. Ты либо пересылаешь их себе, чтобы никогда больше не открыть, либо вырабатываешь баннерную слепоту и просто пролистываешь.
Подход к потреблению информации должен быть не реактивным (пассивное следование), а императивным (целенаправленный поиск). С постепенным развитием включается закон убывающей отдачи: информация начинает приносить меньше пользы, чем реальный опыт. Поэтому эффективнее работает контекстное обучение, а усваивается то, что сразу применяется в работе.
К тому моменту, когда свистоперделка реально пригодится, ее разработчик уйдет в запой, а репозиторий в архив. Все равно придется в моменте искать поддерживаемое решение. Работает это так: вдруг понадобился виджет календаря, заряжаем в o3 промпт следующего содержания: «найди на гитхабе виджет календаря, на compose, проект активно развивается, больше 100 звезд у репо, есть выбор интервала дат». Нейросеть плюется результатами: раз, два и три. Применяем и радуемся.
Баги из рабочей практики
Самая непредсказуемая часть любого приложения – человек за клавиатурой. Нам предопределено хлебать смузи и думать о вечном, сидя на безусловном базовом мешке. А мы ломаем машинные процессы из-за своей иррациональности.
Но пока, именно благодаря ошибкам я открыл для себя автоматические тесты, обязательное код-ревью и канареечную раскатку.
Как баги всплывают на поверхность:
• отловил самостоятельно в процессе разработки
• упал автоматический тест
• не прошли код-ревью
• тестировщик вернул задачу
• прилетело из крэшлитики
• аудит или отчет безопасников
• обратная связь от пользователей
Также пришлось поработать над двумя соцсетями. В каждой был подсайт или публичный топик, где юзеры описывали баги. Эти посты превращались в джира-тикеты. На одной работе был канал в слаке, куда сыпались оценки с отзывами из Google Play. Если ставили одну звезду, кабаныч тегал нас под сообщением.
А вот и сами баги:
1. В старых версиях Android Studio линтер не сообщал, что метод
2. На стартовом экране был какой-то сетевой запрос. Обработку ошибок для него не сделали. Однажды он отвалился совсем. Юзеры запускали апп, пялились на сплэшскрин и не могли навигироваться дальше.
3. Неправильно настроил
4. Был экран на WebView, замаскированный под нативный интерфейс – позже его собирались переделать. Ссылки с этого экрана должны были открываться в Chrome Custom Tabs, но из-за сломанного перехватчика
5. Приложение оперировало квантами, штуками, рублями и копейками. Из-за неправильного округления итоговая сумма расходилась на 1 копейку. Запрос возвращал ошибку. Флоу блокировался. Познакомился с
6. Забыл удалить моковые данные. Бывает бэк прилег, а разработка должна продолжаться. Повесил на кнопку навигацию на экран, недоступный без интернета. В запрос добавил
7. Метод бана юзера в API с параметром
8. Не добавил обработку http-кода 204 и схлопотал
9. Выпустили релиз с
10. Включение вспышки срабатывало с заметной задержкой, потому что вызывало рекомпозицию и пересоздание инстанса камеры. Затащить камеру в Compose было непросто, поскольку API ориентирован на систему View.
11. На экране поста в заголовках добавлялась иконка галочки в конце. Иногда она переносилась отдельно на новую строку, отчего у дизайнера дергался глаз. Я не придумал ничего лучше, чем объединить ее с последним словом: вынул слово, объединил с иконкой в Span, вернул обратно. Работало прекрасно, пока не вышла статья с очень длинным последним словом в заголовке. Контент уехал за пределы экрана.
12. Делал редактор текста с поддержкой markdown: курсив, жирный, жирный курсив и так далее. Неожиданно в сервисе запретили мат. Пользователи стали маскировать его многочисленными звездочками, к чему редактор оказался не готов. Разметка пустилась в пляс.
13. Поехала верстка из-за того, что какая-то библиотека затащила самописный
14. Мутные операторы котлин-коллекций:
Самая непредсказуемая часть любого приложения – человек за клавиатурой. Нам предопределено хлебать смузи и думать о вечном, сидя на безусловном базовом мешке. А мы ломаем машинные процессы из-за своей иррациональности.
Но пока, именно благодаря ошибкам я открыл для себя автоматические тесты, обязательное код-ревью и канареечную раскатку.
Как баги всплывают на поверхность:
• отловил самостоятельно в процессе разработки
• упал автоматический тест
• не прошли код-ревью
• тестировщик вернул задачу
• прилетело из крэшлитики
• аудит или отчет безопасников
• обратная связь от пользователей
Также пришлось поработать над двумя соцсетями. В каждой был подсайт или публичный топик, где юзеры описывали баги. Эти посты превращались в джира-тикеты. На одной работе был канал в слаке, куда сыпались оценки с отзывами из Google Play. Если ставили одну звезду, кабаныч тегал нас под сообщением.
А вот и сами баги:
1. В старых версиях Android Studio линтер не сообщал, что метод
setForeground
появился в Android 6. Вызвал его без проверки уровня API и приложение грохнулось в рантайме на Android 5. Так я узнал, что у нас еще есть такие пользователи.2. На стартовом экране был какой-то сетевой запрос. Обработку ошибок для него не сделали. Однажды он отвалился совсем. Юзеры запускали апп, пялились на сплэшскрин и не могли навигироваться дальше.
3. Неправильно настроил
launchMode
и интенты. У пользователей в Recent Menu отображались две копии нашего приложения.4. Был экран на WebView, замаскированный под нативный интерфейс – позже его собирались переделать. Ссылки с этого экрана должны были открываться в Chrome Custom Tabs, но из-за сломанного перехватчика
shouldOverrideUrlLoading
открывались в самом WebView.5. Приложение оперировало квантами, штуками, рублями и копейками. Из-за неправильного округления итоговая сумма расходилась на 1 копейку. Запрос возвращал ошибку. Флоу блокировался. Познакомился с
BigDecimal
и roundToInt
.6. Забыл удалить моковые данные. Бывает бэк прилег, а разработка должна продолжаться. Повесил на кнопку навигацию на экран, недоступный без интернета. В запрос добавил
delay(10_000)
, чтобы протестировать лоадинг. Моки улетели в репозиторий.7. Метод бана юзера в API с параметром
duration
ожидал либо -1 (пермобан), либо таймстамп в секундах. Из-за kotlin nullable в одной из функций значение «на день» превратилось в -1 и юзеры забанились навсегда.8. Не добавил обработку http-кода 204 и схлопотал
NoSuchElementException
. Запрос выполнился, но выбранная сигнатура была несовместима с пустым телом.9. Выпустили релиз с
NullPointerException
: поспешно добавил ViewBinding в новые фрагменты, не прочитав документацию.10. Включение вспышки срабатывало с заметной задержкой, потому что вызывало рекомпозицию и пересоздание инстанса камеры. Затащить камеру в Compose было непросто, поскольку API ориентирован на систему View.
11. На экране поста в заголовках добавлялась иконка галочки в конце. Иногда она переносилась отдельно на новую строку, отчего у дизайнера дергался глаз. Я не придумал ничего лучше, чем объединить ее с последним словом: вынул слово, объединил с иконкой в Span, вернул обратно. Работало прекрасно, пока не вышла статья с очень длинным последним словом в заголовке. Контент уехал за пределы экрана.
12. Делал редактор текста с поддержкой markdown: курсив, жирный, жирный курсив и так далее. Неожиданно в сервисе запретили мат. Пользователи стали маскировать его многочисленными звездочками, к чему редактор оказался не готов. Разметка пустилась в пляс.
13. Поехала верстка из-за того, что какая-то библиотека затащила самописный
ConstraintLayout
. Исключил транзитивную зависимость через exclude
.14. Мутные операторы котлин-коллекций:
all
, any
и none
ведут себя по-разному на пустых коллекциях, что провоцирует логические ошибки: all{…}
отдаёт true
за счёт «вакуумной истины», когда нет ни одного потенциального контрпримера. any()
возвращает false
, а none()
– true
. Я уже несколько раз на этом обжигался. Еще и курсор любит неудачно применять эти функции в коде.15. При рефакторинге класса-обёртки для RxJava нашёл 40 перегрузок метода
16. ExoPlayer оказался для меня самым проблемным SDK: документация никакая, примеров нормальных не найти. Я не знал, что приложение должно отслеживать входящий звонок, чтобы передавать ему аудиофокус, ставя проигрывание на паузу. У наших пользователей после окончания звонка внезапно воспроизводилось видео из свернутого приложения.
17. Делали кэширование видеозаписей. Забыли про пользователей в роуминге. Посыпались жалобы от туристов, у которых наше приложение съело весь трафик. С тех пор эту историю выслушивают все, кто меня собесит.
18. С Paging Library огреб проблем не меньше, чем с эксоплеером. Прокрутка начала троттлиться из-за неправильного сохранения PagingKey в Room.
19. На бэке добавился новый статус, который в таблице Room хранился в поле с типом
20. Бэк стал слать пустой
21. Поймал неконсистентную эмиссию данных из таблиц Room. Flow нужно собирать в
22. Отвалилось отображение пушей. Потому что загрузил метод
23. Забыл добавить готовые ProGuard-директивы из файла
24. Разнес сборку debug и release на отдельные CI-джобы, но не настроил общий Gradle-кэш. Раннеру не хватило памяти: debug собрался, а release отменился.
25. Для тестирования ML Kit временно добавил в манифест атрибут
26. Загрузил в репозиторий на гитхабе keystore-файл и ключи для подписи релизной сборки, полагая что приватность репозитория гарантирует безопасность.
27. В комментарий к задаче в джире опубликовал фрагмент лога, в котором засветился секретный токен.
28. На одной работе запушил коммит, подписанный корпоративной электронной почтой со второй работы. Теперь не использую
29. Сидел на дейли в AirPods. Вышел встретить курьера, который потерял подъезд. Чтобы расслышать его, нажал кнопку на наушнике – не заметив, что тем самым включил микрофон. Обматерил и курьера и всех на созвоне.
30. Конечно же на первой работе я закоммитился сделать таску за два дня, не проверив доступы. Накануне вечером открываю фигму, а там restricted access. Cпас коллега, у которого были права выдавать инвайты.
Список неполный, постоянно пополняется.
Под конец несколько духоподъемных цитат, чтобы снизить чувство бесполезности усилий:
😠 Не ошибается тот, кто ничего не делает.
😚 Важно не никогда не ошибаться, а побеждать чаще, чем ошибаться.
🥃 Единственная настоящая ошибка – та, из которой ты ничего не извлёк.
onSubscribe
с разными параметрами. IDE подсветила половину как неиспользуемые и я их радостно удалил. В результате вызовы переключились на другие перегрузки, параметры изменились и асинхронщина поломалась.16. ExoPlayer оказался для меня самым проблемным SDK: документация никакая, примеров нормальных не найти. Я не знал, что приложение должно отслеживать входящий звонок, чтобы передавать ему аудиофокус, ставя проигрывание на паузу. У наших пользователей после окончания звонка внезапно воспроизводилось видео из свернутого приложения.
17. Делали кэширование видеозаписей. Забыли про пользователей в роуминге. Посыпались жалобы от туристов, у которых наше приложение съело весь трафик. С тех пор эту историю выслушивают все, кто меня собесит.
18. С Paging Library огреб проблем не меньше, чем с эксоплеером. Прокрутка начала троттлиться из-за неправильного сохранения PagingKey в Room.
19. На бэке добавился новый статус, который в таблице Room хранился в поле с типом
Enum
. Старая версия приложения при чтении передавала эту незнакомую строку в Enum.valueOf()
. Вылет с IllegalArgumentException
.20. Бэк стал слать пустой
id
, надо было в Room для primary-ключа сделать такое: id.ifEmpty { portion }
. Перепутал portion
с локальным полем position
, которое постоянно инкрементируется. Надобавлялась куча дубликатов.21. Поймал неконсистентную эмиссию данных из таблиц Room. Flow нужно собирать в
combine()
, а операции вставки выполнять в единой транзакции.22. Отвалилось отображение пушей. Потому что загрузил метод
onMessageReceived
тяжёлой асинхронной логикой. FCM держит FirebaseMessagingService в памяти недолго, затем прибивает. Об этом я не знал.23. Забыл добавить готовые ProGuard-директивы из файла
missing_rules
, которые указываются при выполнении таски minify
. Релизное приложение вылетело в рантайме.24. Разнес сборку debug и release на отдельные CI-джобы, но не настроил общий Gradle-кэш. Раннеру не хватило памяти: debug собрался, а release отменился.
25. Для тестирования ML Kit временно добавил в манифест атрибут
android:debuggable="true"
. Коммит попал в репозиторий. Специалисты по безопасности потребовали убрать этот флаг из релизной сборки.26. Загрузил в репозиторий на гитхабе keystore-файл и ключи для подписи релизной сборки, полагая что приватность репозитория гарантирует безопасность.
27. В комментарий к задаче в джире опубликовал фрагмент лога, в котором засветился секретный токен.
28. На одной работе запушил коммит, подписанный корпоративной электронной почтой со второй работы. Теперь не использую
git config --global
.29. Сидел на дейли в AirPods. Вышел встретить курьера, который потерял подъезд. Чтобы расслышать его, нажал кнопку на наушнике – не заметив, что тем самым включил микрофон. Обматерил и курьера и всех на созвоне.
30. Конечно же на первой работе я закоммитился сделать таску за два дня, не проверив доступы. Накануне вечером открываю фигму, а там restricted access. Cпас коллега, у которого были права выдавать инвайты.
Список неполный, постоянно пополняется.
Под конец несколько духоподъемных цитат, чтобы снизить чувство бесполезности усилий:
Please open Telegram to view this post
VIEW IN TELEGRAM
Мой топ за свои деньги сяоми расперло от андроид разработки.
Долгие сессии отладки по кабелю плохо сказываются на батарее смартфона. Экран не гаснет, заряд почти всё время у верхней границы. Для литий-ионных аккумуляторов это худшая комбинация. Высокий уровень заряда вместе с повышенной температурой ускоряет деградацию. Батарея вздувается и поднимает крышку.
Предотвратить такое можно беспроводной отладкой, прохладным рабочим местом, снятым чехлом и отсутствием мягких поверхностей. Чем меньше времени устройство проводит на 100% и в тепле, тем дольше прослужит батарея.
Помогают и системные ограничения заряда. В настройках включить всякие там лимиты, protect battery и adaptive charging. Чтобы зарядка останавливалась на 80%.
Даже не знаю, что теперь делать с этим толстячком. Пиксель и самсунги так со мной не поступали. Надеюсь, он не спалит мне квартиру.
Долгие сессии отладки по кабелю плохо сказываются на батарее смартфона. Экран не гаснет, заряд почти всё время у верхней границы. Для литий-ионных аккумуляторов это худшая комбинация. Высокий уровень заряда вместе с повышенной температурой ускоряет деградацию. Батарея вздувается и поднимает крышку.
Предотвратить такое можно беспроводной отладкой, прохладным рабочим местом, снятым чехлом и отсутствием мягких поверхностей. Чем меньше времени устройство проводит на 100% и в тепле, тем дольше прослужит батарея.
Помогают и системные ограничения заряда. В настройках включить всякие там лимиты, protect battery и adaptive charging. Чтобы зарядка останавливалась на 80%.
Даже не знаю, что теперь делать с этим толстячком. Пиксель и самсунги так со мной не поступали. Надеюсь, он не спалит мне квартиру.
Накатил новейший Android 16 на древний Сяоми
И он даже не окирпичился.
Пиксель и прочие благородные устройства могут получать до 7 лет обновлений Android. Остальные, в том числе мой Xiaomi Redmi Note 8T 2019 года, довольствуются меньшим.
Сяоми поставлялся с Android 9 на борту. Потом обновился до Android 10. Потом до Android 11. На этом все. Больше никаких официальных релизов не прилетало.
Когда вышел Andorid 12 я решил взять ситуацию в свои руки. Взломал загрузчик и накатил Android 12. А через год и Android 13. Потом смартфон слегка припух и я его отложил. Но сегодня решил, что хоть он и помирает, с трудом включается и заряжается, стресс-тест в виде установки Android 16 потянет.
Чтобы накатывать новые образы требуется разблокировать загрузчик. Это делается через Mi Account и специальную программу с сайта Xiaomi. Ожидание разблокировки зависит от политики производителя, конкретной модели и региона. Это делается для сдерживания массовой перепрошивки и борьбы с серым импортом. Я ждал 30 дней. Таймер начинает отсчет после первой попытки разблокировки в Mi Unlock. Xiaomi хранит таймер на своих серверах, поэтому обойти его невозможно. Через месяц, при включении устройства появилась иконка открытого замка. Загрузчик взломан.
С устройства убираем блокировку экрана, коды и отпечатки.
В настройках разработчика включаем отладку по USB.
Подключаем смартфон к ПК по проводу.
Скачиваем platform-tools для работы с adb.
Выключаем устройство. Включаем и на 5 секунд зажимаем Power + Volume Down. Попадаем в меню fastboot. На Xiaomi fastboot-режим выглядит как картинка с кроликом, который чинит андроид. В нём нет меню – это просто режим, в котором телефон ждёт команды с ПК по USB.
На ПК в терминале переходим в папку platform-tools и вызываем
Устанавливаем на устройство кастомный recovery. Подойдут TWRP или OrangeFox. Оба хороши, мне больше нравится последний. Скачиваем recovery.img и перемещаем его в папку platform-tools.
Теперь прошиваем кастомный рекавери, чтобы уже из него ставить Andorid 16. Вызываем
В рекавери будет UI-интерфейс. В обязательном порядке нужно сделать очистку данных (Wipe Data). А если переходишь с MIUI или другой сильно отличающейся прошивки – дополнительно Format Data (отдельная вкладка в Wipe, надо ввести yes). Это удалит все из внутренеей памяти и уберет шифрование, из-за которого часто бывает bootloop (бесконечная перезагрузка). После форматирования возвращаемся в главное меню и перезагружаем recovery (Reboot → Recovery), чтобы перемонтировать память.
Теперь скачиваем образ Android 16. Я взял первый попавшийся отсюда. Есть в интернете люди, которым не лень их монтировать. Весит 2GB. Перемещаем его в platform-tools и командой
Образ устанавливается. Смартфон перезагружается. Можно пользоваться.
Все это занимает ~полчаса, просим чатгпт вести по шагам.
Забавно, что ОС думает, что мое устройство это пиксель. Я терпеть не могу прошивки MIUI от сяоми и OneUI от самсунга. Только голый андроид. Только чистый Pixel Experience. Ничего лишнего.
Через год накатим Android 17.
И он даже не окирпичился.
Пиксель и прочие благородные устройства могут получать до 7 лет обновлений Android. Остальные, в том числе мой Xiaomi Redmi Note 8T 2019 года, довольствуются меньшим.
Сяоми поставлялся с Android 9 на борту. Потом обновился до Android 10. Потом до Android 11. На этом все. Больше никаких официальных релизов не прилетало.
Когда вышел Andorid 12 я решил взять ситуацию в свои руки. Взломал загрузчик и накатил Android 12. А через год и Android 13. Потом смартфон слегка припух и я его отложил. Но сегодня решил, что хоть он и помирает, с трудом включается и заряжается, стресс-тест в виде установки Android 16 потянет.
Чтобы накатывать новые образы требуется разблокировать загрузчик. Это делается через Mi Account и специальную программу с сайта Xiaomi. Ожидание разблокировки зависит от политики производителя, конкретной модели и региона. Это делается для сдерживания массовой перепрошивки и борьбы с серым импортом. Я ждал 30 дней. Таймер начинает отсчет после первой попытки разблокировки в Mi Unlock. Xiaomi хранит таймер на своих серверах, поэтому обойти его невозможно. Через месяц, при включении устройства появилась иконка открытого замка. Загрузчик взломан.
С устройства убираем блокировку экрана, коды и отпечатки.
В настройках разработчика включаем отладку по USB.
Подключаем смартфон к ПК по проводу.
Скачиваем platform-tools для работы с adb.
Выключаем устройство. Включаем и на 5 секунд зажимаем Power + Volume Down. Попадаем в меню fastboot. На Xiaomi fastboot-режим выглядит как картинка с кроликом, который чинит андроид. В нём нет меню – это просто режим, в котором телефон ждёт команды с ПК по USB.
На ПК в терминале переходим в папку platform-tools и вызываем
./fastboot devices
. Если все ок – видим наше устройство в списке подключенных.Устанавливаем на устройство кастомный recovery. Подойдут TWRP или OrangeFox. Оба хороши, мне больше нравится последний. Скачиваем recovery.img и перемещаем его в папку platform-tools.
Теперь прошиваем кастомный рекавери, чтобы уже из него ставить Andorid 16. Вызываем
./fastboot flash recovery recovery.img.
После установки перезагружаемся и при включении зажимаем уже Power + Volume Up.В рекавери будет UI-интерфейс. В обязательном порядке нужно сделать очистку данных (Wipe Data). А если переходишь с MIUI или другой сильно отличающейся прошивки – дополнительно Format Data (отдельная вкладка в Wipe, надо ввести yes). Это удалит все из внутренеей памяти и уберет шифрование, из-за которого часто бывает bootloop (бесконечная перезагрузка). После форматирования возвращаемся в главное меню и перезагружаем recovery (Reboot → Recovery), чтобы перемонтировать память.
Теперь скачиваем образ Android 16. Я взял первый попавшийся отсюда. Есть в интернете люди, которым не лень их монтировать. Весит 2GB. Перемещаем его в platform-tools и командой
./adb push android16.zip /sdcard/
отправляем в память устройства. В рекавери переходим в каталог sdcard, выбираем архив и свайпаем для установки.Образ устанавливается. Смартфон перезагружается. Можно пользоваться.
Все это занимает ~полчаса, просим чатгпт вести по шагам.
Забавно, что ОС думает, что мое устройство это пиксель. Я терпеть не могу прошивки MIUI от сяоми и OneUI от самсунга. Только голый андроид. Только чистый Pixel Experience. Ничего лишнего.
Через год накатим Android 17.
В роадмап добавлено еще 49 решений задач с CodeRun. Теперь их там 342.
• График работ
• Кристаллы
• У кого больше королев?
• Шары и корзины
• Интервалы
• Супер мега марафон
• НОД
• Вложенные циклы
• Комбо
• Командная олимпиада
• Среднее в окне
• Создать цикл
• Мега пицца
• Восстановление отчётов
• Жизнь в красках
• Сила искусства
• Хокку на английском
• Фруктовая радуга
• Оставаясь на связи
• Числа в ряд
• Чёрное и белое
• Игра с заменами
• Как курица лапой
• Дерево бонсай
• Классы подобия треугольников
• Шпионы!
• Доска с монетами
• Нужно больше конфет!
• Маленький Саша и круги
• Бутерброды
• Обыкновенная задача про строки
• Число
• Ограничитель частоты
• Клетчатая доска
• Лабораторная работа
• Оптимальный плейлист
• Надежный счетчик
• Бесконечная сумма
• Крош и минимальный путь
• Крош, Ёжик и квадратичная игра
• Новогоднее поздравление
• Ча-ча-ча
• Игра из чулана
• Пицца для вечеринок
• Снежки
• В город на ярмарку
• Открытка с уравнением
• Мандарины и апельсины
• Наряжаем ёлку
#coderun@foundout
Please open Telegram to view this post
VIEW IN TELEGRAM
Решены все задачи из подборки Мобильная разработка первый сезон
• Все дороги ведут в Рим
• Теория вознаграждений
• Страна с разноцветными дорогами
• Самая сложная буква
• Магическая подстрока
• Слова, в которые играют люди
• Найти склад
• Ошейники и песики
• Уникальные пользователи
• Прокачай героя
• Мягкие подстилки
• Максимизация прибыли
• Цветные прямоугольники
• Руководители
• Расселение спортсменок
• Архивирование
• Добавить минусы
• Наташа-рукодельница
• Чётность соседей
• Нормализация показателей
• Курьер Василий
• Поворот
• Достопримечательности
• Простая подсказка
• Салон Health & Sun
• Перелёт
• Игра
• Оценка
• Проверка палиндрома
• Восстановить матрицу
• Кодирование длин серий
• MEX
• Программа стендапа
• Сумма различных
#coderun@foundout
• Все дороги ведут в Рим
• Теория вознаграждений
• Страна с разноцветными дорогами
• Самая сложная буква
• Магическая подстрока
• Слова, в которые играют люди
• Найти склад
• Ошейники и песики
• Уникальные пользователи
• Прокачай героя
• Мягкие подстилки
• Максимизация прибыли
• Цветные прямоугольники
• Руководители
• Расселение спортсменок
• Архивирование
• Добавить минусы
• Наташа-рукодельница
• Чётность соседей
• Нормализация показателей
• Курьер Василий
• Поворот
• Достопримечательности
• Простая подсказка
• Салон Health & Sun
• Перелёт
• Игра
• Оценка
• Проверка палиндрома
• Восстановить матрицу
• Кодирование длин серий
• MEX
• Программа стендапа
• Сумма различных
#coderun@foundout
Решены все задачи из подборки Мобильная разработка второй сезон
• Села батарейка
• Запускайте гуся
• Суеверный коллекционер
• Игра Максима
• Игра в города
• Журнал без дат
• Жизнь в красках
• Фруктовая радуга
• Восстановление отчётов
• Крош, Ёжик и квадратичная игра
• Крош и минимальный путь
• Индекс септаккорда
• Как курица лапой
• Баг в БД
• Вперёд в прошлое
• TOS23
• Крош и строка
• Доставка от Йолли
• Оставаясь на связи
• Анализ данных
• Чёрное и белое
• Градиент
• Сокращение маршрута
• Числа в ряд
• Хокку на английском
• Игра с заменами
• Сила искусства
• Села батарейка (2.0)
• Журнал без дат (2.0)
• Индекс септаккорда (2.0)
#coderun@foundout
• Села батарейка
• Запускайте гуся
• Суеверный коллекционер
• Игра Максима
• Игра в города
• Журнал без дат
• Жизнь в красках
• Фруктовая радуга
• Восстановление отчётов
• Крош, Ёжик и квадратичная игра
• Крош и минимальный путь
• Индекс септаккорда
• Как курица лапой
• Баг в БД
• Вперёд в прошлое
• TOS23
• Крош и строка
• Доставка от Йолли
• Оставаясь на связи
• Анализ данных
• Чёрное и белое
• Градиент
• Сокращение маршрута
• Числа в ряд
• Хокку на английском
• Игра с заменами
• Сила искусства
• Села батарейка (2.0)
• Журнал без дат (2.0)
• Индекс септаккорда (2.0)
#coderun@foundout
Подпись для Debug APK
При обновлении APK тестировщики могут столкнуться с ошибкой «конфликтует с другим пакетом». Придется удалять предыдущую версию приложения, чтобы установить новую. Данные в кэше и сохраненные настройки потеряются.
Если не настроить signingConfig, сборка будет автоподписываться локальным ключом из домашней директории пользователя. Он хранится на устройстве, но легко теряется при сносе кэша или смене пользователя. На облачных раннерах домашняя директория и файл также могут не сохраняться между запусками. При каждой сборке будет генерироваться новый ключ. APK не будет обновляться поверх установленного.
Для debug-сборок нужно создавать файл подписи, как для релиза. В Android Studio Build → Generate Signed App Bundle or APK. После заполнения данных выбрать Debug. Операционная система сравнит APK по отпечатку подписи и разрешит обновление.
При обновлении APK тестировщики могут столкнуться с ошибкой «конфликтует с другим пакетом». Придется удалять предыдущую версию приложения, чтобы установить новую. Данные в кэше и сохраненные настройки потеряются.
Если не настроить signingConfig, сборка будет автоподписываться локальным ключом из домашней директории пользователя. Он хранится на устройстве, но легко теряется при сносе кэша или смене пользователя. На облачных раннерах домашняя директория и файл также могут не сохраняться между запусками. При каждой сборке будет генерироваться новый ключ. APK не будет обновляться поверх установленного.
Для debug-сборок нужно создавать файл подписи, как для релиза. В Android Studio Build → Generate Signed App Bundle or APK. После заполнения данных выбрать Debug. Операционная система сравнит APK по отпечатку подписи и разрешит обновление.
Как менять иконку запуска Android-приложения
Пользователям приложения можно предоставить возможность выбирать иконку лаунчера. Это делается для кастомизации, сезонных акций или платного функционала закрытого пейволом.
Реализовать можно через Activity Alias – единственный универсальный механизм подмены иконки. Они должны быть заранее импортированы в проект. Адаптивные векторы с атрибутом <monochrome> будут перекрашиваться в цвета обоев, независимо от выбранного иконки.
Aliases (псевдонимы, точки входа) прописываются в манифесте и ссылаются на существующую активити. Они могут иметь собственный интент-фильтр MAIN+LAUNCHER, иконки и другие параметры. Операционная система найдет псевдоним при парсинге манифеста и добавит ярлык в лаунчер. Чтобы в лаунчере отображалась только одна иконка – один алиас включаем (android:enabled="true"), остальные отключаем.
Управление алиасами осуществляется через PackageManager. Получить текущее состояние алиаса можно вызвав getComponentEnabledSetting. А установить – через setComponentEnabledSetting. Когда мы выбираем иконку, фактически один псевдоним включается, а остальные отключаются. Если иконка еще не выбиралась – отобразится дефолтная из манифеста. После выбора процесс приложения будет убит. Иконка обновится сразу. Есть лаунчеры, которые ее кэшируют и процесс займет до 500 мс.
Если по каким-либо причинам все алиасы отключаются (сброс настроек приложения, переустановка из стороннего маркета, ошибочное обновление) нужна безопасная инициализация, иначе пользователь потеряет ярлык в списке приложений. Для этого в Application.onCreate() нужно пройтись по всем алиасам и если ни один из них не включен – активировать дефолтный.
Я записал воркшоп, как это делается на практике:
🐗 Смотреть на Ютубе
🐗 Смотреть на Бусти
🐗 Исходники на Гитхабе
Пользователям приложения можно предоставить возможность выбирать иконку лаунчера. Это делается для кастомизации, сезонных акций или платного функционала закрытого пейволом.
Реализовать можно через Activity Alias – единственный универсальный механизм подмены иконки. Они должны быть заранее импортированы в проект. Адаптивные векторы с атрибутом <monochrome> будут перекрашиваться в цвета обоев, независимо от выбранного иконки.
Aliases (псевдонимы, точки входа) прописываются в манифесте и ссылаются на существующую активити. Они могут иметь собственный интент-фильтр MAIN+LAUNCHER, иконки и другие параметры. Операционная система найдет псевдоним при парсинге манифеста и добавит ярлык в лаунчер. Чтобы в лаунчере отображалась только одна иконка – один алиас включаем (android:enabled="true"), остальные отключаем.
Управление алиасами осуществляется через PackageManager. Получить текущее состояние алиаса можно вызвав getComponentEnabledSetting. А установить – через setComponentEnabledSetting. Когда мы выбираем иконку, фактически один псевдоним включается, а остальные отключаются. Если иконка еще не выбиралась – отобразится дефолтная из манифеста. После выбора процесс приложения будет убит. Иконка обновится сразу. Есть лаунчеры, которые ее кэшируют и процесс займет до 500 мс.
Если по каким-либо причинам все алиасы отключаются (сброс настроек приложения, переустановка из стороннего маркета, ошибочное обновление) нужна безопасная инициализация, иначе пользователь потеряет ярлык в списке приложений. Для этого в Application.onCreate() нужно пройтись по всем алиасам и если ни один из них не включен – активировать дефолтный.
Я записал воркшоп, как это делается на практике:
Please open Telegram to view this post
VIEW IN TELEGRAM
Добавлено 64 новых эмодзи:
Все паки: первый • второй • третий • четвертый • пятый • шестой
Сайт и миниапп с названиями, поиском и фильтрами
#emoji@foundout
Please open Telegram to view this post
VIEW IN TELEGRAM