Давно хотел попробовать разработку мобильных приложений под Android. Не “поиграться с туториалами”, а написать что-то настоящее - чем можно реально пользоваться. При этом не очень хотелось погружаться в Kotlin1 - там своя атмосфера: синтаксис, сильно отличающийся от C-подобного, свои пакеты и библиотеки…
А тут как раз Microsoft выпустила .NET MAUI2 - фреймворк для написания мобильных приложений на C#. Для меня это было очень кстати: C# я знаю давно, и, хочется надеяться, кое-что на нём умею. Оставалось только придумать, что написать. Думал я довольно долго. Хотелось, чтобы приложение закрывало какую-то мою (или общую) боль, чтобы у меня было желание возвращаться к разработке, внедрять новые фичи и править баги.

Тут нужно отступить немного в сторону - в сторону велосипедных покатушек. Раньше я часто (сейчас пореже) выезжал на велосипеде в компании довольно-таки случайных людей. Мы никак не были связаны - ни номерами телефонов, ни мессенджерами. Возникали задачи: как найти друг друга? Кто-то отстал - он едет, сошёл с дистанции, или упал в канаву и тонет?… Существует довольно много сервисов для решения таких задач - Google Maps, Glympse, Telegram и прочие - но они всегда требовали чего-то вроде “добавления в друзья”, и/или регистрации. Иногда сервисы устанавливали лимиты вроде “30 минут трансляция бесплатна, далее - за денежку”. Плюс, довольно чувствительные данные - твоё местоположение - в целом уходили непонятно куда.

А хотелось буквально: нажал кнопку - трансляция пошла, нажал другую - ссылка отправилась, компаньон её открыл - и сразу видит тебя на карте. Меньше бюрократии - вот чего хотелось.

Так появилась идея собрать своё. И нет, это был не Roadnik, погодите, до него ещё не дошли. 😁
Есть такое приложение - Locus Map. Это настоящий швейцарский нож в мире приложений-карт. Не буду перечислять его достоинства, статья не о нём; но я пользовался им, даже подписку оплачивал. Одна из функций этого приложения - отправка текущего местоположения на web-сервер, используя GET- или POST-запросы. Ну я и написал примитивное PHP-приложение, которое умело эти данные принять, а потом отобразить на карте. Оно было ужасно примитивное. Например, маршруты хранились в .txt-файлах. 😅 Но оно работало. Однако не хватало нескольких критических функций: оповещения о старте трека кем-то, удобного шаринга в один клик, да и Locus Map был далеко не у всех, и настройка его занимала немало времени. В общем, своё, но очень неудобное.

И в какой-то момент я решил сделать полностью независимый от чужих приложений сервис, главное свойство которого - простота и быстрота. Так и родился Roadnik. Как же он работает?

Основная сущность - это комната (room): общее пространство, идентифицируемое произвольным ключом3. К ключу есть требования по длине и допустимым символам, но в остальном пользователь может сам его выбрать. Хочешь - полностью рандомная строка, а хочешь - “super-event-2026-05-20”. Кто знает ключ - может участвовать. Никаких аккаунтов. Приложение при первом запуске само генерирует длинный случайный ключ - делиться им можно через QR-код или как обычной ссылкой прямо из приложения. Ссылка сама определяет, что делать: если приложение установлено — сразу открывает комнату, если нет — предлагает скачать.

В комнате могут быть несколько участников с разными никами. На карте они отображаются отдельными маркерами и линиями. Нажав на маркер, видишь скорость, заряд батареи, уровень сигнала и несколько других параметров участника. Если кто-то подключил BLE-пульсометр4 - видишь ещё и пульс. Это пришло в голову в какой-то момент как повод изучить BLE. Теперь полноценная фича.

drawing

Совсем недавно появилась возможность включить интеграцию с Discord, и тогда во время шаринга местоположения статус в Discord показывает примерную локацию, скорость и пульс со ссылкой на карту (Discord Rich Presence5).

drawing

Ещё на карте можно создавать просто метки. Например, это может быть полезно, если нужно указать место встречи, которое заранее неизвестно. Поставил метку, назвал её “ВСТРЕЧАЕМСЯ ЗДЕСЬ!” - и всё понятно.

Про приватность. Сервер полностью опен-сорс и self-hostable6. Можно поднять у себя - тогда данные вообще никуда не уходят (правда, для адаптации MAUI-приложения придётся повозиться с FCM8 и прочими интеграциями). Сам держу публичный экземпляр на roadnik.app, бесплатный и без регистрации (c). Там есть лимиты по частоте запросов и количеству хранимых точек, но для обычного использования их вполне хватает.

Для интересующихся - несколько слов о технической стороне. Сервер - .NET 10, ASP.NET Core, SQLite. БД встроенная, т.к. с текущей нагрузкой её достаточно. Веб-карта - Vue 3 + TS + Leaflet, поставляется вместе с сервером. Мобильное приложение - .NET MAUI под Android: внутри та же веб-карта, встроенная в WebView. Одна кодовая база карты работает и в браузере, и в приложении - получилось удобно. Docker-образ7 сервера опубликован на Docker Hub с поддержкой linux/amd64 и linux/arm64.

Подытоживая: Roadnik начался как способ разобраться с MAUI. Потом как-то само собой оброс фичами: тепловые карты Strava, поддержка карт Thunderforest, пульс, Discord Rich Presence…
И всё работает так, как хочу я, и как нужно мне. Именно поэтому писать свои проекты куда приятнее, чем чужие.

Код - на GitHub, приложение - в Google Play.


  1. 1.Kotlin - основной язык для нативной Android-разработки
  2. 2..NET MAUI (Multi-platform App UI) - фреймворк Microsoft для разработки кросс-платформенных приложений под Android, iOS, macOS и Windows на C#
  3. 3.Ключ выступает как "пароль" от комнаты - длинная случайная строка
  4. 4.BLE-пульсометр - пульсометр с беспроводным подключением по Bluetooth Low Energy
  5. 5.Discord Rich Presence - функция, позволяющая сторонним приложениям отображать кастомный статус активности в мессенджере Discord
  6. 6.Self-hosted - развёрнутый на собственном сервере
  7. 7.Docker-образ - упакованная версия приложения для запуска в контейнерной среде Docker
  8. 8.Firebase Cloud Messaging - сервис, с помощью которого в т.ч. доставляются push-сообщения на мобильные устройства