MixUpload — история разработки

В этой статье я попробую рассказать вам всю историю разработки MixUpload. Мою историю, а не всю целиком.

Итак, дело было летом, когда я, уставший от офисной работы (хотя устал я от нее еще зимой 2018) искал себе удаленную работу. Стоит уточнить, что я устал тогда не только от того, что ты ездишь в офис 5 дней в неделю, но еще и от режима работы — по 40 часов в неделю. Так что летом я был в поисках работы удаленной и на полставку — 20 часов в неделю.

Стоит сказать, что эта задача была не из самых простых. Найти работу в офисе на полный рабочий день проще простого (ха-ха, ну не совсем). Если у тебя опыт работы несколько лет, то не составит особого труда найти работу (вот хорошую работу — действительно непросто).

Но как это бывает в жизни, если чего-то очень хотеть (и не просто хотеть, а что-то делать для этого) то оно рано или поздно случится. Летом этого года (2019) познакомился с MixUpload. Я перешел в гугл плей маркет и сразу было все понятно — очередное приложение с оценкой ниже 4, в отзывах гневные комментарии недовольных пользователей. Но в отличие от никчемных и никому ненужных приложений оно мне показалось подающим надежды. Как никак приложение для прослушивания музыки (и да, не просто плеер, где ты слушаешь свою музыку, а ту, которая доступна в сервисе п.с. можно загружать свою, если вы DJ). И что самое приятное было на тот момент — можно было слушать без рекламы и ограничений в фоне (что и сейчас доступно, да. Никакой рекламы, никаких ограничений на прослушивание в фоне). Как ни странно, я залогинился и наткнулся на один трек — Zivert — Шарик (remix).

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

Итак, 12 августа я начал кодить. Как и всегда бывает — начальство хотело просто улучшить стабильность приложения — уменьшить количество крашей и т.д. А крашей там как и всегда было хоть отбавляй (помню в июле 2018 когда начал разрабатывать Ewa там тоже было дофига крашей и легаси говнокод, куда же без него). Кароче — посмотрел я легаси говнокод и сразу сказал — не, ребят, такое дерьмо нельзя быстро подлатать. Давайте перепишем все с нуля. Что бы вы понимали, там легаси было аж с 2014 года. Джава, джейсонпарсинг руками и 100500 строк кода в активити, фрагментах и непонятных хелперах.

На секунду вернемся к парт-тайм. Итого мы договорились, что я буду работать 15 часов в неделю. И это было прям супер. 3 часа поработал и все, можно остальное время отдыхать. И вообще этим я хотел доказать всем и каждому — 40 часов в офисе не надо сидеть, достатчно 15 часов в неделю удаленно. Люди не кодят по 8 часов подряд и не могут быть эффективны на долгой дистанции. Выгорание при полном рабочем графике неизбежно. Потому что мало того, что ты по 8 часов в офисе официально, то и учитывая специфику нашей работы (релиз, хотфиксы и вот это все) ты там можешь просидеть и все 10 и 12 (да да, поверьте мне, было время я работал с 9 утра и до 9 вечера). И плюс необязательно общаться с людьми офлайн. Люди такие существа, что офлайн общение может быть, скажем так, дольше чем нужно. Можно одну мелочь расмусоливать часами. Помню мы собирались для обсуждения какой-то фичи(фигни) на 5 минут и затягивалось на полтора часа. Да, товарищи, нечего языком чесать, надо работать. Ну и плюс онлайн общения в том, что ты можешь ответить на вопрос в чате тогда, когда тебе будет удобно. Ненавижу, когда сидишь в офисе, работаешь и к тебе приходят и отвлекают. Митапы всякие непонятные, на полтора часа. Ну нафиг опенспейсы. Дома хорошо. Беруши в уши, закрыл чаты и ебашь код пока время не вышло. Успеть за 3 часа. И знаете что? Я почти всегда успевал закодить фичу за 3 часа. Меня эти 4 с лишним месяца научили четко планировать свое время. Я точно знаю что я могу успеть за 3 часа непрерывного кодинга.

Теперь давайте я вам расскажу об особенностях самого приложения и с какими трудностями мне пришлось столкнуться за это время. Первая трудность это конечно же логин. Чтобы вы понимали, апи фейсбука постоянно меняется и из-за одного лишнего пермишена, который нынче депрекейтед не работал логин. С ВК особого дерьма не было, кроме того, что тебе надо дебаг ключ закинуть в консоль чтобы дебажить и обновив до свежей версии поменять импорты. Но еще кроме соц.сетей в приложении есть вход по почте и надо подтвердить свой аккаунт. Здесь у нас работают диплинки и вся эта радость. Вроде несложно, но всякого дерьмеца можно хавнуть. Адепты сингл активити скажут — ну да, придется сделать новое активити. Потому как юзер может перейти из почты не только когда нужно, но и когда не нужно. Сами понимаете, юзеры они такие.

Но это все мелочи. Вот после логина начинается самое сложное. Да, забыл сказать. Перед тем как начать кодить нужно решить какой стек технологий юзать. С начала 2019 года я плотно пересел на Kotlin. Так что с выбором языка не пришлось думать. Так же несколько месяцев до MixUpload я кодил использую MVVM + liveData от Гугл. Так что и здесь вопросов не возникало. Оставался вопрос с DI и навигацией. Я долгое время работал с Dagger2, после чего был период когда юзался Koin, но в какой-то момент времени я решил что буду пилить так, как мне хочется, без этих либ. И знаете что, уже больше полугода я юзаю свой велосипед и прекрасно себя чувствую. По поводу навигации — изначально я не парился о ней, ну переходишь на новый фрагмент и супер. Один период я работал с гугловой навигацией — ну ничего так, пойдет. Но в последние 2 приложения я так же решил не использовать чужих решений и написал свой навигатор, который юзает fragment manager. И знаете что? Чувствую себя прекрасно.

Итак, стек выглядит так — Kotlin+coroutines, MVVM+LiveData ну и все. Ну да, Picasso для загрузки изображений, тут уже не стал писать свой велосипед.

Теперь перейдем к трудностям и задачам которые приходилось решать. Первое что нужно было решить — списки треков. Дело в том, что в приложении есть несколько разделов — топ 100, лента, поиск, жанры, свои плейлисты. Так же есть список текущих треков, т.е. то, что ты начал слушать. И в первую очередь нужно было придумать способ хранить треки и в юай и в плеере. Долго думая я придумал такое решение — один репозиторий для юай, другой для плеера. В одном храним те треки которые видны юзеру, в другом храним очередь треков на прослушку. И да, треки в плеере надо хранить не только во время сессии, но и после смерти приложения. Т.е. вы всегда можете открыть приложение и продолжить слушать тот трек который слушали с той позиции где прервались и те треки, которые решили тогда прослушать. По поводу юай — здесь нужно было хранить треки как минимум при смерти и восстановлении процесса в течение сессии. Все кто более менее долго кодят знают про режим разработчика Don’t keep activities. И да, событие сохранения стейта происходит в реальной жизни не так редко как вам кажется. Для этих целей я всегда на тестовом девайсе включаю этот режим. И первая трудность была именно в том, что нужно хранить треки. И если с треками плеера проблем нет особо — можно записать в БД (для этих целей я заюзал Realm, да да знаю, он тяжелый все дела, но я привык к нему, так как юзаю с 2016 года), то казалось бы что треки юай можно просто в бандл положить — но нет. Если на вкладке топ 100 всего 100 треков и они скорей всего поместятся в бандл, то в ленте их может быть овердофига — 600+. Так что класть в реалм пришлось как треки юай так и плеера.

Идем дальше. Следующая трудность заключается в синхронизации. Изначально я сделал так, что у меня синхронизировались и плеер с юай и юай с плеером. Но это было ошибочное решение. Так как юай может обновиться в течение сессии (например вы поменяли настройки ленты и перестали слушать Dubstep, а начали слушать например Deep House), значит при переходе на ленту нельзя из репозитория плеера отправлять на юай треки. Они уже неактуальны. Собственно поэтому сейчас синхронизация в одну сторону. Т.е. вы например начали слушать топ 100 (кстати, треки грузятся кусками, не сразу все 100, а по 20 штук и везде) и если вы скролите список и загружаете следующие треки, то они должны добавиться и в плеер тоже. Для этого у меня в методе получения треков есть синхронизация — где проверяется айди репозитория юай и того, который в плеере.

На самом деле это действительно сложная задача, так как для каждой страницы есть свои параметры загрузки. Если в топ 100 только один плейлист, то в жанрах их много. Вы можете слушать один жанр, но в нем слушать топ треков или топ миксов или новые треки и выбирать дату рейтинга. Т.е. при сохранении нужно учитывать некие лишние данные. Для этого конечно же нужна фабрика репозиториев (ха-ха, нет) где будет собираться полный пакет данных для отображения актуальной информаци и сопоставляться с тем списком треков, который в плеере. Т.е. не просто проверить что в плеере и на юай у тебя жанры играют, а еще надо проверить какой плейлист — поджанр, дата, тип.

Дальше мы перейдем к таким страшным вещам как действия с треком. Абсолютно любой трек, на абсолютно любом экране можно лайкнуть (swipe reveal layout), тогда надо обновить значок лайка и сохранить его в свой плейлист лайки. Здесь трудность заключается в том, что у нас может быть открыто несколько экранов и не забываем о текущем треке и текущем плейлисте. Т.е. вы можете условно лайкнуть один трек на одном скрине и значок сердечко должем поменяться до 4 мест одновременно. Еще одно действие это добавление трека в плейлист. Такая же история. Все это решил с помощью 2 механизмов — как прозвали в чате андорид — свой реактивный евентбас и конечно же ливдата.

Проблема с навигацией это отдельная история которая заслуживает отдельного поста — у вас всегда что-то может играть в плеере и у вас всегда есть возможность перейти на эту страницу и посмотреть что именно и какая очередь треков. Значит, что эту страницу нужно делать уникальной и не добавлять в стек каждый раз. Этим грешат многие приложения, например ВК, Яндекс музыка и иже с ними. Т.е. ты переходишь в текущий трек/плейлист, оттуда куда-то еще и еще раз открываешь текущий трек/плейлист и при нажатии назад видишь несколько раз экран текущего трека — на мой взлгяд это не хорошо и я решил сделать по-своему. Ты никогда не увидишь в нашем приложении дубликат экрана текущего трека. Ровно так же ты не увидишь дубликат экрана детали трека (там полная информация и возможность оставить комментарий к треку). Что я сделал? А просто при каждом переходе на текущий трек убирал из стека его и ставил в конец очереди. Вот и все. Потому что у тебя всегда есть шторка уведомлений, откуда ты можешь перейти на страницу текущего трека и плейлиста.

Ладно, вроде с юай и навигацией разобрались. Следующая сложность это офлайн. Да, если вы премиум юзер то у вас есть возможность не только создать свой плейлист, но и прослушать его офлайн. И это тоже отдельная история — у вас может быть безмерное количество плейлистов, их всегда можно менять на лету и со смартфона и с веба. Значит при каждом изменении нужно обновлять треки офлайн. В настройках приложения доступна возможность выбрать количество треков офлайн — 20, 50 или 100. А это значит, что при любом изменении настроек надо догрузить или удалить треки. Так же в вашем плейлисте может быть условно 200 треков. Значит первые 100 максимум можно слушать офлайн. Но если вы убираете верхние треки которые офлайн из списка, то нужно заново скачать те треки, которые были после сотого. Еще одна замечательная особенность, скачивать одновремнено несколько плейлистов или их менять. Да, у меня работает форграунд сервис где отображается информация что мы скачиваем прямо сейчас. Но так же нужно реактивно обновлять юай. Ты скачал один трек, нужно отобразить значок скачано на треке в юай. И неважно на каком фрагменте вы находитесь. Если скачанный трек есть в топ 100, то он тоже будет отмечен знаком доступно офлайн. Задача решается просто тем, что при каждом появлении нового трека офлайн я обновляю юай через лиснер. На самом деле еще много нюансов здесь есть, но вы и сами можете это все продумать (оборвался интернет, юзер отменил скачивание, преревалось скачивание из-за выключения смартфона — кстати, если такое произошло, то при следующей сессии треки будут продолжаться скачиваться оттуда где прекратились, нужно всего лишь открыть приложение, хотя можно было бы просто ловить событие включения устройства и появление интернета).

Итак, треки в юай, треки в плеере, навигация текущий трек плейлист, что еще трудного было на моем пути за это время? Само проигрывание музыки. И если вам кажется что это просто, ха ха ха. Нет, это не так. Юзаю ExoPlayer от гугла. Но знали бы вы сколько нюансов. Например нужно разруливать событие, когда наушник из смартфона вытаскивается — тогда музыка не должна орать на всю маршртуку (BecomeNoisyReceiver). Так же нужно чтобы при входящем звонке ваша музыка прекратилась, а если у вас например навигатор говорит — через 200 метров поверните направо, то нужно свою музыку приглушить. Это называется AudioFocus. Следующая особенность заключается в том, что нужно дать возможность управлять вашей музыкой с Bluetooth девайса. Для этих целей я приобрел китайскую колонку, на которой есть все функции — следующий трек, предыдущий, плей и пауза. И знали бы вы сколько сил на это ушло, ахахаха. Но это еще не все. Вдруг тебе говорят, что есть автомобилисты и там должна отображаться информация о том, какой трек играет, на какой секунде. И если можно купить блютус устройство за 500 рублей, то машину по-быстрому не купишь с магнитолой, хе-хе. Кстати, здесь стоит упомянуть добрых людей, которые со мной работали на первой работе, Винярский, спасибо за помощь.

Еще одна интересная история это шторка уведомления и конечно же наши любимые китайфоны сяоми. На самом деле в нормальных устройствах таких как Google Pixel когда ты прерываешь музыку — можно одним смахиванием убрать уведомление из шторки. А в сяоми нельзя. Вот и вся история. Для этого например во многих плеерах есть четвертая кнопка — крестик. Нажав на нее можно просто закрыть уведомление, даже если у тебя сяоми и миуи. Но и здесь не все так просто. Суть в том, что уведомление бывает 2 видов (на самом деле больше) — стандартное и расхлопнутое. И если в расхлопнутом можно сделать действительно большое уведомление и спокойно расположить кнопки (след.трек, предыдущий, плей/пауза и крестик), то в стандартном виде места маловато. А надо еще обложку альбома показать, автора и тайтл кроме всего прочего. Суть в том, что на экране блокировки отображается стандартный вид, чтобы его развернуть придется свайпнуть вниз. Чем я собственно и руководствовался. Начиная с версии 2.5.2. у вас на локсрине будет стандартное уведомление без возможности убрать если у вас сяоми. Но нужно просто свайпнуть вниз и тогда будет расширенная версия и там нажать крестик. Вуаля.

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

Что я вам хочу сказать напоследок — если бы мне сказали, что я буду разрабатывать приложение музыки год назад, я бы посмеялся. Есть одна категория приложений, которая мне не нравится откровенно — навигаторы и карты. А музыка сама по себе сложная вещь. Мне кажется любой разработчик понимает что сделать действительно хорошо работающий плеер сложно. А если у вас не просто плеер, а еще и совмещенная соц.сеть, то сложней втройне.

Да да да, в приложении у тебя есть аккаунт, на тебя могут подписаться другие юзеры, ты можешь писать сообщения и делиться треками с другими юзерами. Можешь добавлять юзеров себе в друзей. К слову мессенджер это тоже непростая задача, поэтому в текущем релизе 2.5.2. его нет. Оставили на потом. К слову о том, что такое подписаться на юзера — каждый юзер может создать плейлист и добавлять туда треки. Любой другой юзер может подписаться на него и в его ленте будут отображаться треки, которые есть в плейлисте первого. Ровно так же как и в соц.сетях — ты видишь в своей ленте новостей публикации друзей. Здесь же вместо публикаций — треки.

Наверно, осталось еще несколько историй относительно разработки этого приложения, но я их буду рассказывать по пути. А пока, всем спасибо кто дочитал мою историю, устанавливайте, слушайте хорошую музыку и фидбечьте. Я всегда рад пофиксить пару тройку багов (к слову, после первого релиза обнаружились несколько критических багов из-за особенностей девайсов и версий андроид, которые я пофиксил в первом хотфике, но еще были пожелания от юзеров в первой версии, которые я с удовольствием добавил в следующий хотфикс).
Когда ты не первый год разрабатываешь приложения, то у тебя немного сменяются приоритеты — тебе важно не то, насколько красив твой код, какой процент тестирования (кстати, я не написал ни одного юнит теста, но у нас есть 150+ тест-кейсов на данный момент и тестировщик, который все это проверяет) — тебе становится важным только то — насколько стабильно работает приложение и насколько оно удобно для живых людей — твоих пользователей. Так что да — мои критерии успешного приложения это количество крашей и уровень удовлетворенности юзеров. Мне глубоко наплевать на то, насколько чиста архитектура в приложении, если этим приложением невозможно и неудобно пользоваться.

Так что друзья мои — пишите код для людей, делайте приложения для людей. Как говорил один мудрый человек в фильме Мимино —

Слущай, когда мне будет приятно, я так довезу!.. что тебе тоже будет приятно.

И еще раз ссылка на приложение и конечно же вы можете слушать музыку в вебе — MixUpload.com. Всем приятного кодинга приятной разработки. Я в телеграм — @johnnysc

Запись опубликована в рубрике Программирование Java. Добавьте в закладки постоянную ссылку.