Устали от перегруженных трекеров: как в 21 год я в соло разработал беговое Android-приложение WEIRUN на Jetpack Compose, BLE и Supabase

Устали от перегруженных трекеров: как в 21 год я в соло разработал беговое Android-приложение WEIRUN на Jetpack Compose, BLE и Supabase

От борьбы с китайскими энергосберегайками и парсинга сырых байтов нагрудных датчиков до готового релиза без Google-сервисов. История независимого разработчика.

Большинство современных беговых приложений превратились в перегруженные комбайны.

Картина знакома каждому, кто бегает круглый год: стоишь на улице в дождь или на холодном ветру перед темповой тренировкой, пальцы замерзают, запускаешь трекер — а тебе в лицо летит баннер годовой подписки, предложение купить кроссовки, всплывающая лента чужих постов и три экрана подтверждений. Вместо быстрого нажатия «Старт» ты вынужден бороться с интерфейсом. А после ухода зарубежных сервисов из РФ ситуация стала еще веселее: танцы с бубном вокруг обходов, проблемы с оплатой и внезапные обрывы синхронизации.

Мне как разработчику и бегуну с 9-летним стажем нужен был простой, быстрый и визуально строгий инструмент: моментальный старт, точная фиксация пульсовых зон через нагрудные датчики по Bluetooth (BLE) и полное отсутствие информационного мусора.

Поняв, что идеала на рынке нет, я решил написать свой беговой Android-трекер — WEIRUN — в одиночку.

Ниже история того, как за 8 месяцев проект вырос из чистого экрана Android Studio в боевой софт: с какими граблями я столкнулся при работе с Bluetooth SIG, почему Android безжалостно глушит GPS на 10-м километре и как в соло закрыть бэкенд без миллионных бюджетов.

Почему нативный Android, а не кроссплатформа?

Когда заходит речь об MVP, многие сразу тянутся к Flutter или React Native: мол, один код на две платформы, экономия времени. Но когда проект завязан на:

  1. Непрерывную работу Foreground Service с высокоточным сбором GPS;
  2. Постоянный опрос Bluetooth Low Energy (BLE);
  3. Агрессивную политику убийства фоновых процессов китайскими оболочками (MIUI, HyperOS, ColorOS, EMUI);

— любая абстракция кроссплатформы начинает сыпаться. Приходится писать такое количество нативных бриджей на Kotlin, что смысл кроссплатформы теряется.

Устали от перегруженных трекеров: как в 21 год я в соло разработал беговое Android-приложение WEIRUN на Jetpack Compose, BLE и Supabase

Поэтому стек был выбран бескомпромиссно нативный:

  • Язык: Kotlin
  • UI: Jetpack Compose (Single-Activity, декларативный UI)
  • Архитектура: Clean Architecture + MVI/MVVM на StateFlow и SharedFlow
  • Фоновые службы: Foreground Service + кастомные уведомления
  • Датчики: Android BLE API (Bluetooth Low Energy)
  • Бэкенд: Supabase (PostgreSQL, Row Level Security, Auth)

Грабли №1: Подключение нагрудных пульсометров (BLE) и парсинг битов

Для атлета, который тренируется по пульсовым зонам (а не бегает в анаэробном угаре на каждом кроссе), подключение нагрудного датчика (Polar, Garmin, CooSpo, Magene) — критическая функция. Встроенные оптические датчики на смартфонах и дешевых браслетах безбожно врут при ускорениях.

Устали от перегруженных трекеров: как в 21 год я в соло разработал беговое Android-приложение WEIRUN на Jetpack Compose, BLE и Supabase

Все спортивные пульсометры работают по стандартному профилю Bluetooth SIG: Heart Rate Service (0x180D) и характеристике Heart Rate Measurement (0x2A37). Казалось бы — подключайся и читай. Но в реальности сырые данные прилетают массивом байт ByteArray, где каждый бит имеет значение:

  • Первый байт — сервисные флаги.
  • Если нулевой бит флага равен 0, пульс передается в формате UINT8 (1 байт, значение до 255 уд/мин).
  • Если флаг равен 1, пульс летит в UINT16 (2 байта). Если не реализовать эту ветку, то на пиковом ускорении при пульсе выше 255 (или при сбое датчика) приложение просто упадет с IndexOutOfBoundsException прямо во время тренировки.
  • Следующие биты определяют, есть ли в пакете данные о контакте с кожей, потраченной энергии и RR-интервалах (вариабельность сердечного ритма).

Пришлось написать собственный надежный парсер, который на лету декодирует битовые маски и раскладывает пакет на чистую модель данных с учетом смещения байтов (offset):

val flags = data[0].toInt() val isHeartRateFormat16Bit = (flags and 0x01) != 0 val isRrIntervalPresent = (flags and 0x10) != 0 var offset = 1 val heartRate = if (isHeartRateFormat16Bit) { val value = (data[offset].toInt() and 0xFF) or ((data[offset + 1].toInt() and 0xFF) shl 8) offset += 2 value } else { val value = data[offset].toInt() and 0xFF offset += 1 value }

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

Грабли №2: Фоновый режим и битва с китайскими энергосберегайками

Худший кошмар любого бегуна: ты пробежал полумарафон, достаешь телефон, а трекер заснул на 3-м километре, потому что оболочка решила сэкономить 0.5% заряда батареи.

Как эта проблема была решена в WEIRUN:

  1. Изолированный Foreground Service: Логика тренировки вынесена в отдельный сервис с явным системным типом foregroundServiceType="location | connectedDevice". Это говорит системе Android: «Этот процесс жизненно важен, он держит связь с внешним оборудованием и пишет координаты».
  2. Оптимизированный Notification: Системное уведомление обновляется каждую секунду через NotificationCompat, транслируя текущий темп, дистанцию и ЧСС прямо в шторку и на Always-On Display. При этом сам Compose-экран в фоне спит и не нагружает GPU.
  3. Локальный буфер (Offline-First): Любые координаты и пульсовые точки сначала пишутся пачками в локальную SQLite (Room). Даже если телефон попал в глухую зону без связи или сеть упала — ни один метр тренировки не потеряется. Синхронизация с сервером происходит пачками после восстановления коннекта.

Грабли №3: Почему не свой бэкенд на Ktor/Spring, а Supabase?

Когда ты работаешь в соло, распыляться на поддержку собственного микросервисного зоопарка, настройку Kubernetes и ручные миграции баз данных — верный путь похоронить проект через 2 месяца от выгорания.

Я выбрал Supabase и ни разу не пожалел:

  • Честный PostgreSQL под капотом: никаких ограничений документных баз, полноценная реляционная модель данных для аналитики тренировок.
  • Row Level Security (RLS): права доступа к тренировкам настраиваются прямо на уровне таблиц базы. Физически невозможно перетереть или прочитать чужие данные, даже если кто-то попытается дернуть API напрямую.
  • Бесшовная авторизация: готовая система сессий и токенов из коробки, не требующая поддержки собственного auth-сервиса.

Эстетика и геймификация: интерфейс, в котором хочется оставаться

Беговой софт не должен выглядеть как скучная бухгалтерская таблица. В дизайн WEIRUN я заложил строгую, темную эстетику с неоновыми акцентами, которая отлично читается на ярком солнце через спортивные очки и не бьет по глазам в сумерках.

  • Карточка тренировки: визуализация аэробных и анаэробных зон пульса, расчет каденса, темпа и расхода калорий в один скролл.
  • Награды и 3D-медали: система достижений за ключевые старты (например, стартовая медаль «Первая волна»), которые можно сразу экспортировать в сторис в один клик.

Релиз в 2026 году: жизнь без Google Play

Публикация мобильного софта в текущих реалиях для инди-разработчика из России превратилась в отдельный квест. Рассчитывать только на Google Play бессмысленно, поэтому дистрибуция сразу строилась через альтернативные каналы: RuStore, Huawei AppGallery и прямые APK-релизы.

Главный вывод: архитектуру нужно изначально проектировать без жесткой привязки к Google Play Services. Все карты, пуши и аналитика должны работать независимо, чтобы сборка запускалась на любом устройстве — от чистого AOSP до HarmonyOS.

Что дальше?

WEIRUN уже перешагнул стадию идеи: приложение стабильно работает на боевых пробежках, держит связь с нагрудниками и пишет точные треки. Впереди — расширение социального функционала, внедрение соревновательных сегментов и умной аналитики восстановления.

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

Буду рад адекватной критике архитектуры, фидбеку по UI и идеям от тех, кто сам разрабатывает под Android или регулярно выходит на пробежки.

Как вам интерфейс и подход к BLE? Каких фич вам больше всего не хватает в современных спортивных трекерах? Давайте обсудим в комментариях.

Скачать можно в RuStore и AppGallery:

33