Warning: Undefined array key 0 in /var/www/tgoop/function.php on line 65

Warning: Trying to access array offset on value of type null in /var/www/tgoop/function.php on line 65
174 - Telegram Web
Telegram Web
Осталась финальная тема, собрать воедино путь от момента нажатия на иконку приложения до появления первой Activity. 

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

Перехватив нажатие на иконку приложения, мы говорим системе, что нужно запустить новую Activity. Раньше это делалось так: берем имя пакета приложения, из Context получаем нужный нам Intent для запуска первой Activity и запускаем будто это наша Activity. Сейчас так не делают, для запуска первой Activity из лаунчера нужно использовать специальный сервис LauncherApps. Сделано так потому что начиная с 8 Android изменились правила по работе с пользователями. В него мы передаем пакет приложения и координаты иконки на экране, которые нужны для показа красивой анимации старта приложения.

Как вы уже знаете у нас всегда работает связка Manager -> SystemService. Поэтому все вызовы методов LauncherApps отправляются в LauncherAppsService, который в основном занимаете проверкой разрешений, ищет от какого пользователя открылось приложение вот это все и дальше это все отправляется в волшебный класс ActivityStarter. 

ActivityStarter это конечная точка запуская любой Activity, каждый раз когда вы запускаете Activity, причем неважно это ваша Activity или другого приложения все вызовы приходят в ActivityStarter. В ActivityStarter есть всего один метод execute, в котором и располагается вся логика. Работа с ActivityStarter чем-то напоминает работу с запросами при помощи OkHttp. Аналогично, большой билдер с настройками запроса и потом один метод который запрос исполняет.

В ActivityStarter располагается логика по обработке флагов и launchMode. Вот эти singleTop, singeTask и т.д все это рассчитывается в ActivityStarter и уже он решает какие Activity будут убраны, а какие добавлены. Помимо этого разруливает в какой Task будет добавлена Activity. ActivityStarter занимается лишь расчетом как должна быть запущена Activity, сам запуск Activity уже проводят другие сервисы об этом есть инфа в прошлом посте. 

Дальше происходит проверка если ли активный процесс данного приложения. Ведь если он есть, это означает что приложение просто было свернуто и не нужно запускать новый процесс. Если же процесса нет, то через специальные волшебные статические функции из класса ZygoteProcess через метод startViaZygote мы подаем сигнал тому самому бесконечному циклу в ZygoteInit для запуска нового процесса и одновременно с этим показываем сплэш скрин.

Сплэш мы показываем начиная с Android 12, а в версиях до этого, показываем window background темы первой Activity. За показ старта приложения всегда отвечает StartingSurfaceController  он же и запоминает как выглядит приложение при сворачивании, чтобы потом показать его в Recents.  В момент когда вы сворачивали приложение система запомнила как выглядел последний экран, поэтому при разворачивании приложения система рисует анимацию основываясь на этой информации.

На этом все, я наконец-то закончил эту серию. Я надеюсь у вас сложилась более менее общая картина. Подвести итог можно такой, что в коде Android много интересных концепций которые можно использовать в работе. Знать все названия классов и т.д нет никакого смысла, потому как все постоянно меняется. Скорее всего со следующим релизом, то что я описал тут будет уже не актуально.
👍32🔥101
Привет всем. Итак я буквально недавно закончил саму тяжелую для меня серию. Тяжелая она потому как сама по себе это тема Rocket Science и от этой темы не такой большой отклик и эмоциональный и по просмотрам. Помимо этого я немного выпал почти на 2 недели, у меня был отпуск и максимальная прокрастинация из-за темы про Android. Сейчас посты будут выходить чуть чаще, и я думаю возможно некоторые будут выходить без картинок, потому как именно они и занимают львиную долю времени.

В ближайшее время я не буду делать разборов как работает та или иная технология сложная технология под капотом и делать больше делать постов на приземленные темы. Как мне кажется от этого и вам будет больше пользы. На работе я сменил команду на Platform-Tech которая занимается сборками, автоматизациями и CI, скорее всего рано или поздно сделаю посты на эти темы.
👍499🔥4
Чтобы стать хорошим инженером, желательно под капотом знать как устроена технология с которой ты работаешь каждый день. Это отличный совет, но часто забывают указать границу, когда копать технологию вглубь не нужно и даже порой вредно. Универсальный совет по проведению этой границы. Если есть уверенность, что знание технологии вглубь тебя ускорит, каеф продолжай. Если же не уверен, сходи и займись чем-то другим.

Основная моя мысль в том, что порой знание как работает технология под капотом вообще никак не отражается на твоей работе. Например, мы на собеседованиях часто спрашиваем как работает GC. Правда спроси у любого разработчика любого уровня как повлияло это знание на его работу, ничего суразного он не скажет. Аналогичная история с сортировками, структурами данных вроде деревьев и графов и т.д. Единственная причина почему стоит знать как под капотом работает GC это если вы хотите делать этот самый GC. 

Эта же история и с тем как под капотом работает Android или как система дергает ЖЦ Activity. Как развлечение узнать как там это устроено прикольно, но вот когда это становится требованием это странно. Проблема этих знаний в том, что вы можете знать как работает Android, но ничего не сможете с этим сделать, если вы не разработчик Google.  

Глубина технологий которые мы используем похожа на Марианскую впадину. Каждый раз копая сорцы какой-то сложной технологии создается иллюзия, что вот-вот ты уже почти достиг дна, еще чуть-чуть и ты преисполнишься в своем сознании, и этот мир станет тебе абсолютно понятен. Однако достигнув глубины ты неожиданно понимаешь, что это был всего лишь выступ и погружаться до самой сути нужно еще. Это бесконечная бездна, и всей жизни не хватить, чтобы постигнуть все. 

К чему это графоманство. Я помню что когда был джуном, знал всякие пазлеры котлина, как работает оптимизация для обертки примитивов в Java. Нюанс в том, что меня про это даже на собесе ни разу не спросили. Порой мы тратим время на изучения всего и вся лишь для того, чтобы потом не испытывать стыд на собесе, что мы чего-то не знаем. Собеседование не экзамен, расслабьтесь.

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

1️⃣ причина, самая основная. Вам за эту глубину платят или знание этой глубины позволить вам делать ваши задачи быстрее. Вот парочка примеров. Знать как работает многопоточность нужно, это позволит избежать багов, на которые потом потратите недели чтобы их найти. Знать как сохраняется ViewModel нужно, потому как легко можно потерять состояние и опять-таки наплодить багов. 

2️⃣ причина. Вас про эту технологию спросили на собесе, но вы смогли ответить только поверхностно. Поэтому ходите на собесы хотя бы раз в год, даже если вы уже работаете для актуализации знаний. Однако тут со здравым скептицизмом, если вас на собеседовании на позицию Android разработчика спрашивают про версии gcc, то это не вы дурак это в компании душные идиоты и нахер такую компанию. 

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

Подводя итог, как не потратить время на копание вглубь технологии, знания о которой никуда не уперлись. Включаете меркантильность на максимум, если за глубину платят мы изучаем, если нет то забиваем.
39👍13👎1
😁14👎1🤔1
Итак, в прошлом посте был вопрос “А как понять, что интервьюер не знает, о чем спрашивает?”. Вопрос довольно прикольный, поэтому я решил по-быстрому накидать пост про собесы. Как я уже говорил, собеседование это нефига не экзамен, это лишь проверка сработаетесь ли вы вместе или нет. Отсюда выходит две вещи. 

Первое – если вы чего-то не знаете то пофиг, нестрашно. Даже в собесе на сеньорскую позицию никто не будет ожидать, что вы знаете прям все. Главное не пытайтесь придумывать и тянуть время если не знаете и еще очень круто когда вы отвечаете в стиле: “Я эту тему не копал, но могу предположить, что это работает так и так”. Цель собеса не понять знаете ли вы что-то, а умеете ли думать. И еще не гуглите ответ во время собеса, это охренеть как палевно, у вас скорее всего не получится это сделать незаметно.

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

Собеседующие могут ошибаться и нести чушь. Вы его можете поставить на место, а он потом обидится и напишет в фидбеке в графе soft skills что вы мудак. Поэтому сначала пройдите собес, а уже потом можете в личку прийти к нему и сказать что он не прав 😁

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

Смотрите я много раз был со стороны и собеседующего и собеседуемого. В сумме я провел примерно собесов 50 и прошел штук 15 и я точно могу сказать, что никто не знает, как правильно собесить программистов. Любое собеседование это лишь некоторая игра, и вы либо удовлетворяете правилам игры, либо нет. Ни один собес не сможет показать на сколько вы хороший программист. Но тут как с капитализмом, его никто не любит, но лучше пока ничего не придумали. 

Очень часто пройдете ли вы собес или нет решает именно то, как вы себя продаете, нежели то, насколько вы умный. Дальше еще желательно смотреть на соотношение “запарности” собеса и бонусов которые дает компания. Пример: если компания хочет 50 этапов собесов с лайф кодингом и при этом платит ниже рынка, нахер такую компанию.  

Совет как научится проходить собесы. Все довольно просто, вам нужно их проходить. Вот тут реально нет смысла читать про прохождение собесов или на долго их откладывать. Самый крутой буст который вы можете получить, это сходить на 3-4 собеса и там провалится, поверьте я проверял это работает. 

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

После собеса не забывайте спрашивать собеседующих. Я вообще не понимаю людей, которые приходят на собес и после вообще ничего не спрашивают. Хотя казалось бы, можно же кучу вопросов задать, по технологиям, по компании по процессам. Это те вещи которые вам как разработчику должны быть капец как важны, вы же собираетесь в этом месте провести довольно продолжительное время.
24🔥6👍5
Ну и напоследок. После собеса всегда просите фидбек, даже если молчат не стесняйтесь закидывать сообщениями HR по поводу фидбека. В идеале всегда должен быть результат с собеса, это либо фидбек, либо офер. Если HR долго не приходит с фидбеком, то тут два варианта. Либо у HR жесткая запарка и они просто медленные, либо компания мутная контора. Крутые компании всегда дают фидбек. Если компания на вас забила и не дала вам фидбэк, значит это хорошо что вы туда не попали. Потому как если с HR процессом у них все грустно, значит и в остальном тоже будет плохо.

 P.S компании maang вроде как забивают на фидбэк, но это исключение 😄
17👏2
ViewModel

👇
👍17
{1/2} Сразу скажу, что не вижу смысла просто копировать документацию к ViewModel. Потому как с ними работать и как их использовать уже есть тысячи статьей и я просто внизу приведу список того, что считаю более менее норм. Тут я хочу просто разобрать основную проблему которую они решают, рассмотреть как они работают под капотом и в чем разница м/у тем же Moxy. 

Итак, погнали. Проблема, которую решает ViewModel – сохранение состояние экрана без сохранения в Bundle. Например, из-за размера или по причине того, что в Parcelable не сохранишь вещи вроде Disposable. Разумеется разработчики задумались, а каким образом можно сохранить данные между переворотами. 

Вначале использовали retain fragment о которых мы уже говорили. По понятным причинам от этой идеи начали отказываться и стали все просто сохранять в статику, которая живет пока, живет весь процесс приложения. Сохраняли кто как умеет, крутые ребята делали через Scope DI, не крутые прям так напрямую и сохраняли в статические поля. Все понимали что разработчики нуждаются в хорошем удобном инструменте для решения этой задачи. 

И пара пацанов из no name галеры запилили Moxy. Библиотека Moxy взлетела на расцвете архитектуры MVP. Разумеется были и другие похожие решения, но это был самый хайповый инструмент. Сейчас это уже считается пережиток прошлого и некоторые компании даже стесняются признаваться, что у них он до сих пор используется. Недостатка у Moxy всего два это кодогенерация и сильная завязка на архитектуру MVP. 

Значит как работала Moxy. Вы унаследовали свой Presenter и Activity базовых классов, затем ставили специальную аннотацию на полем презента в Activity и происходила магия кодогенерации, благодаря которой этот презентер переживал смерть активити между переворотами. Вся магия сводилась к тому, что презентеры тупо сохранялись в статическую HashMap. 

Спустя какое-то время, Гугл после увидел, что у разработчиков есть нужна в адекватном инструменте для сохранения состояния и запилила ViewModel. Разработчикам из гугла потребовалось 8 лет чтобы это увидеть. С такой скоростью адекватное API для клавиатуры мы увидим примерно к 28 году. 

В 2017 году пришел Гугл и сказал что MVP параша, и нормальные ребята используют MVVM. Помимо этого, они любезно как раз сделали все для использования этого подхода. MVVM это не разработка гугла, это придумали Microsoft еще тогда когда у всех были Nokia, но об этом как-нибудь потом.
👏25👍4😁3
{2/2} По началу все с презрением относились к этой технологии, потому как тогда было модно хейтить Гугл за их неудобные и багующие либы. В целом за эти 5 лет ничего не поменялось. Однако за это время все признали ViewModel и по сути сейчас это основной способ хранить данные м/у убийствами View и весьма удобный. 

ViewModel удобен тем, что можно делать хоть по 100 мелких на Activity или фрагмент. Плюс к этому есть метод для очистки, который точно дернется системой когда ViewModel умрет. Однако есть пара минус в том, что для проверки работы ViewModel нужно прям крутить экран, потому как Moxy можно было проверять при помощи настройки “Don’t keep Activities”. А ViewModel ломаются с этой настройкой, потому как считают что в этом случае ты сам ушел с экрана.

У ViewModel явное преимущество над другими решениями в том, что код по работе с ViewModel встроен в Activity. Встроен не в обычную Activity, а в ту которая идет с Jetpack. На самом деле встроен не сильно, там лишь добавили пару методов для удобства, при желании можно сделать свою реализацию ViewModel.

ViewModel работает несколько хитрее нежели Moxy. Они сохраняются не в статике, а в ActivityRecord. В предыдущих постах я писал про ActivityThread, которая умеет кучу всего. Так вот, в этом самом ActivityThread хранится список ActivityRecord, это некоторые данные по Activity, которые будут жить пока мы с Activity не уйдем, т.е даже между поворотами. 

Как я уже сказал выше, мы можем сделать нашу собственную реализацию ViewModel. Для этого нужно использовать метод переопределить метод onRetainNonConfigurationInstance. Если вы унаследовали свою Activity от любой Activity которая идет в Jetpack, у вас не получить переопределить этот метод, потому как в Activity Jetpack этот метод помечен как final.

Однако поиграться все равно можно, через метод onRetainCustomNonConfigurationInstance (не путайте с предыдущим). В этом методе нужно вернуть объект, какой вашей душе угодно. Допустим будем возвращать HashMap<String, CustomViewModel>. После всех переворотов, мы можем получить наши данные через метод getLastCustomNonConfigurationInstance. Вот так, можно легко и просто сохранить данные без Bundle.

Ровно такой механизм и используют ViewModel. ViewModelStore (который используется в ViewModelProvider) это буквально и есть HashMap<String, ViewModel>. Разумеется если ваш процесс умрет, умрут и данные сохраненные через onRetainNonConfigurationInstance, и соответственно все ViewModel, но вы это и так знали.

Метод onRetainCustomNonConfigurationInstance помечен как deprecated, и вместо него гугл рекомендует использовать ViewModel. По мне так это правильно, не хотелось бы приходя на проект видеть велосипед с этим методом. 

Значит теперь пара советов по использованию ViewModel. Не страдайте фигней и сразу подключайте ktx и используйте делегаты. Дальше за вас все сделает система, главное правильно интегрируйтесь с DI. Большинство DI сами умеют работать с ViewModel. Что почитать:

👉 Официальная дока по ViewModel это must have и в целом там очень доступно все написано
👉 Тоже с официальной доки, туториал который должен помочь

Ну и совет, не тащите LiveData, предпочитайте Kotlin Flow или Rx.
Вот теперь вопрос к публике: вспомните дурацкое поведение фрагментов, и задумайтесь, а очистится ли ViewModel в таком фрагменте?
👏28👍6
LiveData

👇
👍9
В прошлом посте я набросил на LiveData. Сейчас хочу описать почему я считаю LiveData не самым крутым решением. Давайте начнем вообще с проблемы, из-за чего появилась LiveData. Хочу тут заострить внимание, что само по себе решение довольно неплохое, оно конкретно мне не нравится и в посте постараюсь объяснить почему.

Как вы помните MVVM в целом базируется на идее о том, что у нас есть View и есть ViewModel. View подписывается на изменения с ViewModel и когда получает события изменяет элементы на экране и т.д. После того как Google стала пропагандировать MVVM встал вопрос о том, каким образом реализовать обсервабельность.

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

В момент появления ViewModel моду набирало реактивное программирования. Подход когда у тебя есть потоки данных и ты декларативно это все описываешь всем пришелся по вкусу. В то время как раз начала расцветать RxJava. Однако у RxJava есть два основных минуса, почему ее не стали использовать для передачи событий из ViewModel на View.

Первый минус это ее сложность. RxJava действительно довольно сложная либа в плане понимания, особенно для начинающих. Сложная в основном потому, что нужно думать потоками данных, а в начале все думают императивно. Второй минус по дефолту RxJava не умеет отписываться когда умирает View и это нужно делать вручную, что довольно неудобно. Поэтому Гугл решить эту проблему сделав свою урезанную версию RxJava. LiveData решает все проблемы описанные выше и создает парочку своих.

Первая проблема, которую создает LiveData архитектурная. Эта проблема выходит из одного золотого правила: “Если можно наговнокодить с либой, это будет сделано рано или поздно”. LiveData идеальна для передачи данных от ViewModel к View. Все на этом идеальность кончается, однако в проектах где я был и где использовалась LiveData, она волшебным образом проникала на Domain слой. Причем Гугл в этом даже помогает, они даже в room добавили возможность возвращать LiveData чтобы следить за изменениями.

Чем плохо что LiveData может оказаться в Domain слое? В Domain слое речь идет о бизнес логике, а значит и о применении кучи различных операторов. У LiveData максимально всратое API для операторов, она тут дико проигрывает RxJava и Flow. Помимо этого LiveData очень сильно завязана на View, нельзя подписаться не на UI потоке и без View. Конечно можно подписаться без View, но только нельзя будет отписаться, прям как от email рассылок.

Ну и вторая проблема это тесты. Опять таки из-за того, что LiveData очень завязана на UI нужно во первых реализовать свою мини версию Handler чтобы вообще можно было тесты запустить. Во-вторых нельзя просто так сделать функцию которая например ждет пару секунд, и если событий нет падает. В Rx и Flow такое делается довольно просто и интуитивно, в LiveData нужно выдумывать что-то вроде такого.

Ну и естественно как же вспомнить проблему SingleLiveEvent, для которой Гугл уже 5 лет уперто не желает выпускать мелкий классик, и каждый раз нужно пилить свой.

Подводя итог. LiveData прикольный инструмент и довольно мощный. Его можно тащить в свои тестовые, его можно тащить в мелкие проекты, особенно когда вы только начали изучать разработку. Однако в уже большие проекты или когда вы уже себя уверено чувствуете лучше затаскивать RxJava, а лучше FLow потому как сейчас для View есть много расширений по работе с корутинами.
👍29👎1
🎋 Дополнение к LiveData

Сейчас я работаю над интересным постом про DRM, однако пост занял больше времени чем я рассчитывал. Поэтому чтобы вы не скучали, я вам докину еще один недостаток про LiveData, про который недавно узнал.

У LiveData есть два оставновных метода которые позволяют отправить в нее данные: setValue и postValue. Разница в том, что setValue пытается просто заметить данные в поле, и поэтому если вы вызовете этот метод не на UI потоке он упадет. Второй метод postValue уже можно использовать из любого потока, под капотом он просто вызываем метод setValue, но через Handler.

В прошлом после я уже упомянул сложности в Unit тестировании. Эти сложности как раз выходять из того, что LiveData создает Handler, который нельзя создать вне эмулятора. Эта сложность решаемая, т.к разработчики позволили заменить этот класс который под капотом использует Handler. Однако это не все сложности которые приносит метод postValue.

Вторая проблема связана с обновлением данных LiveDate. Если у LiveData нет подписчиков, то метод postValue не будет обновлять данные до тех пор, пока они не появятся. Другими словами, вы используете LivaData, чтобы допустим, хранить состояние, и вы хотите получать эти данные и на UI и в самой ViewModel.

Пока UI активен все хорошо, однако как только UI умирает, вы не можете получить свежие данные из LiveData. При обращении к полю вы будете получать старые данные, а это может привести к ох каким неплохим багам.

Почему сделано именно так? Ответ на этот вопрос я не знаю, возможно разработчики решили так избежать лишней нагрузки на UI поток. Мне показалось, что вам будет интересно знать про такое поведение.
👍43👏1
DRM

{1/2} Недавно в разговоре со знакомым разработчиком из Кинопоиска он упомянул DRM. Я раньше никогда не слышал про эту штуку, но как оказалось знать про нее must have если вы хотите работать с медиа: фильмы, музыка, книги и т.д.

Значить суть, представьте вы разработчик приложения для музыки. И рано или поздно у вас встанет вопрос, а как сделать защиту контента, который слушают или смотрят ваши пользователи. Смотрите, каждый раз когда вы прослушиваете музыку, плеер скачивает файл с музыкой, как минимум для того, чтобы быстро начать его воспроизведение если мы вернёмся назад. Помимо этого у каждого подобного приложения есть фича, позволяющая скачать файл явно чтобы после была возможность прослушать его offline.

И вот тут возникает вопрос, ведь можно накачать музыки и потом просто забрать эти файлы. Как минимум не составит большого труда зайти на флешку через File Manager и эти файлы забрать. Естественно разработчики давным давно задумались о том, а как вообще защитить подобного рода данные. Ответ на этот вопрос DRM.

DRM - digital rights management набор технических средств которые ограничивают копирование. Разрешить чтение файла, но при этом запретить копирование задача не тривиальная и в некотором смысле почти недостижимая. С этой системой все так или иначе сталкивались когда например, пытались сделать копию диска с игрой. В детстве я помню, что на некоторых дисках была защита из-за которой просто так нельзя было скопировать игру. В общем и целом, все вот эти защиты которые часто используют игры и есть DRM.

У DRM много противников, которые утверждают что это бесполезная дичь, потому как один фиг все взломают и скопируют. И много последователей, потому как ограничивая копирования вы защищаем авторские права и не теряем бабки. Можно понять обе эти группы, но нас тут интересует именно инженерная часть, а именно то, как это реализовано в Android.

DRM это общее название, конкретная система гугла называется Widevine. Однако сам Android поддерживает и другие схемы DRM, но их рассматривать не будем, ибо это скучно. Ну так вот, как это работает. Значит этот Widevine помогает зашифровать потоковый контент так, чтобы его нельзя было просто так взять и скопировать. Работает он с двумя основными протоколами, Dash и HLS. Подробнее о них расскажу как-нибудь потом, сейчас просто запомните эти протоколы помогают сделать потоковую передачу данных.

Значится, в систему Android прям на уровне системы вшит код по работе с DRM. Причем даже есть специальный фреймворк для вендоров, которые могут делать свои схемы DRM если им захочется. Другими словами это аппаратная штука и ломануть ее мамкин хацкер уже просто так не сможет. Как схематично работает эта DRM.

Гугл на своих серверах хранит лицензии контента, которые мы как создатели контента туда кладем. Помимо этого предоставляет инструменты, при помощи которых мы свой контент нарезаем на чанки, которые в последствии будем отдавать с наших серверов.

Вот вы сидите в приложении для музыки, нажимаете на иконку песни. В этот момент приложение отправляет запрос к вашему серверу на скачивание данных песни и получает первый файлик который называется InitData, в нем вшита информация о том, какая это схема DRM и еще всякая разная метаинформация. Как я уже упоминал выше, в большинстве случаев это будет Widevine.

Далее мы c этой InitData идем в системный сервис MediaDrm и получаем запрос. Получаем мы его в виде байтового массива. Берем этот запрос и по Http клиенту идем на сервер гугла, который хранит нашу лицензию. Сервер проверят запрос и отдает нам лицензию.

После мы берем эту лицензию и отправляем в MediaDrm. Далее инициализируем специальный класс MediaCodec который и отвечает на расшифровку контента. Теперь когда все настроено мы можем наконец проигрывать песню. Скачиваем небольшую часть песни которая зашифрована, передаем данные в MediaCodec он расшифровывает мы играем. И так на каждый чанк (часть файла музыки или видео). Чтобы играть офлайн, нужно при помощи специальных методов указать MediaDrm что лицензию необходимо сохранить, чтобы использовать позже.

Продолжение ниже 👇
👍26🔥31🤔1
{2/2} Примерно так это все и работает. Куча этапов и куча проверок на каждом этапе, поэтому просто так скачать видос и сохранить не получится. Помимо этого, так как MediaDrm работает на уровне системы (см. картинку) это позволяет сделать вещи вроде запрета скриншотов и записи экрана. Разумеется эти схемы обходятся, они не идеальны, но точно защитят контент от большей части пользователей.

На каждый трек и каждый видос защищённый при помощи Widevine ваш клиент делает запрос на сервера Гугл за лицензией. Что это означает? Что гугл может в любой момент не отдавать лицензию если ему ваш IP показался странным. Может на уровне операционной системы сделать так, что определенный контент вы не сможете смотреть легально. Работникам Кинопоиска или Яндекс Музыки система DRM приносит много сюрпризов и не самых положительных отзывов 😄

Если вы прочитали все это и ужаснулись, что сколько всего нужно знать, чтобы просто показать видос на устройстве не парьтесь. За вас это все уже сделано в Exoplayer, вам только его нужно передать правильные ссылки.
👍24🔥2
Прокрастинация идеального кода
👏12🔥3
Когда я был молодым и глупым у меня была идея крутой библиотеки, для работы с протоколом STOMP. Если кто не знает протокол STOMP это просто набор правил для работы с подписками на данные, который работает поверх Websocket. У этого протокола очень простая спецификация и он очень удобен когда нужно что-то сделать быстро и не охото придумывать свой велосипед.

Для бэкенда и фронта есть целый набор готовых решений. Однако для Android готовых решений нет. Хотя конечно есть, но чаще всего это забагованные, неудобные решения, разработчики которых давно забили на поддержку. И вот, казалось бы, я нашел крутую идею для библиотеки, которая решает реальную проблему. Можно просто сделать библиотеку с более менее удобным API и стать вторым Джэйком Вортоном. 

Радостный сажусь я пилить эту библиотеку. Разумеется я как хороший разработчик сначала должен разработать крутую архитектуру библиотеки, чтобы не стыдно было показать. Затем желательно не завязываться на rx и корутины, а сделать отельные модули для этого, как у настоящих крутых библиотек. 

Я придумал архитектуру. Как мне казалась, учел все принципы SOLID, KISS, YAGNI, FizzBuzz, CQS. После начал придумывать API, ведь библиотекой должно быть максимально удобно пользоваться. Думал очень долго иии так и не придумал. Библиотеку в итоге я так и не сделал. 

Причина почему я перестал делать либу, заключается в том, что у меня началась дикая прокрастинация. Садясь разрабатывать либу, я думал не о том как быстрее решить задачу, а том как сделать супер гибкую архитектуру и код который было бы не стыдно показать. 

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

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

После я конечно понял почему код тех библиотек был такой крутой и обрабатывал все возможные случаи. Просто эти библиотеки уже долго на рынке и ими пользуется куча людей. Их код переписывался множество раз, и фиксилось куча багов которые прилетали от других разрабов. Серьезно вспомните сколько версий у RxJava, у OkHttp? 

Невозможно сразу при создании какого-то решения учесть все, особенно когда у вас нет большого опыта. Поэтому лучше всего сначала что-то сделать, попробовать этим попользоваться. И только потом, если решение багует и оно неудобное начать его улучшать. Только в этом случае вы будете точно представлять что конкретно нужно улучить. 

Крутыми разработчиками не становятся думая над архитектурой 3 года и потом выдавая супер мега решение со всеми корнер кейсами. Это работает с религией или философией, но в разработке это происходит так: Сделали какое-то решение -> Обосрались -> Порефлексировали  -> Исправили косяки -> Повторили

Причем это работает не только с разработкой библиотек. В бизнесе это называют MVP. Так работает Agile, так развиваются архитектуры в уже существующих проектах. На текущем проекте где я работаю уже 3-я версия архитектуры. Это при том, что это одна из топовых мобильных команд в России. 

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

Подводя некоторый итог. Хотите быстрее двигаться, делайте что-то, даже если не знаете как сделать хорошо все равно делайте.
👍36🔥123
Представьте, вы смогли вернуться назад во времени, к себе только начинающему изучать программирование. Какой совет вы бы себе дали? 

У нас же есть куча принципов и best practice. Возьмем например SOLID, несколько принципов составленных в одно слово. Кстати есть слух, что когда Мартин с друзьями собирал эти принципы, принципов было больше, они просто взяли те, из которых получалось слово, а остальные решили опустить). Правда я не проверял насколько это правда.

Проблема принципов SOLID в том что они очень абстрактные. У них может быть несколько трактовок и они не дают конкретики. Я точно несколько раз был свидетелем срачей по поводу того, как правильно трактовать принцип открытости/закрытости. Про принцип подстановки Барбары Лисков я вообще молчу, тут запутались вообще все. У принципов SOLID естественно есть критика советую прочитать. Некоторая критика, притянута за уши и как всегда правда где-то посередине, но стоит ознакомиться со всеми точками зрения.

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

Есть ли более конкретные принципы, которые  стоит использовать, чтобы легко улучшишь свою кодовую базу? Смотрите, я уже делал посты про применение концепций функционального программирования и CQRS. Применив эти две концепции вы уже сможете писать гораздо более поддерживаемый и чистый код. К этим двум вещам, я бы еще добавил еще один.

Не бойтесь создавать объекты. Очень частая ошибка, особенно у начинающих, что они забывают про то, что у нас ООП язык и используют концепции процедурного языка. Отсюда появляются куча функций с более чем 5-ю параметрами, дублирование, избыточность, куча мутабельных полей и вот все эти головные боли. 

Совет как по мне очень простой, давайте на примере. Нужно в функцию передать 4 параметра? Сделайте специальный объект для этого. Из функции нужно вернуть пару значений вместо одного? Лучше сделать свой объект, а не стандартный Pair. Со стандартным Pair легче запутаться, особенно когда объекты одного типа. 

Когда мы используем объекты, мы зажимаем в некоторые тиски то, что можно передать в нашу функцию или класс. Другими словами, мы полагаемся на систему типов, которая за нас сможет отловить баги. Поэтому лучше отдавать предпочтение объектам, а не примитивам, когда появляется более менее сложная структура. 

Самый банальный пример тут это дата. У вас есть несколько вариантов как хранить дату в объекте, которую вы например получаете с бэка.
Первый вариант, это просто строка с уже форматированной датой. Второй вариант это конкретный объект например LocalDateTime. 

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

Часто мы пугаемся создавать объекты по причине того, что они якобы занимают память и нагружают GC. Вот тут вообще можно забить (если речь не идет о методе onDraw), это было проблемой на ранних версиях Android. Сейчас GC очень умный, настолько что порой даже создание ObjectPool получается дороже, чем просто создать объект.
👍28🔥21
🎛 Многомодульность

Когда я только начал работать в качестве Android разработчика, моей первой компанией был аутсорс. Это была очень маленькая компания и в основном были мелкие заказы на проекты длительностью 2-3 месяца. Довольно часто я был единственным разработчиком на проекте. Я ничего не слышал про многомодульность, что это и зачем. 

В этот же период был расцвет докладов про архитектуры. Тогда только начинали говорить про многомодульность. Я помню как смотрел доклады от разработчиков Яндекс Карт, Lift, Uber идея многомодульности казалась невероятно крутой. Разумеется я подумал, раз крутые ребята так делают на своих проектах то и я так должен делать.  На основных проектах времени на это особо не выделяли и поэтому многомодульность можно было попробовать только на своих pet проектах.

После просмотра всех этих докладов казалось что многомодульность это вообще серебряная пуля от всех проблем и все проекты сразу должны быть многомодульными по дефолту. Это так называемый джуниорский перфекционизм. Когда у тебя в руках оказывается молоток ты повсюду начинаешь видеть гвозди. Многомодульность и правда очень крутой инструмент, правда решает он определенные задачи. Порой когда речь идет о многомодульности путают причину и следствие. Сейчас я попробую вам рассказать когда нужна многомодульность, а когда это просто разрабы решили поиграться.

Есть такая штука как Закон Конвея. Суть такая, что архитектура ПО повторяет архитектуру организации в которой это ПО создается/используется. Другими словами, архитектура в приложении повторяет архитектуру бизнеса.

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

Чаще всего это будут и разные команды разработки. Если над Android приложением работают 40 разработчиков, это не значит, что они прям вместе работают. Они разбиваются на отдельные команды по 2-3 человека и каждый пилит свою часть. Разумеется в этом случае в нашем приложении нужно делать отдельные модули. Потому как это разные экраны и развиваться они должны независимо. Грубо говоря это разные приложения, которые работают в рамках одного процесса и имеют одинаковые UI элементы (если дизайнер адекватный). В одном модуле мы бы просто сошли с ума, постоянно исправляя конфликты, и задевая код другой команды.

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

И поэтому довольно забавно слушать истории, когда ребята из аутсорса, при заказе приложения на 3-4 месяца, без дальнейшей поддержки затаскивают многомодульность. Она там не нужна, многомодульность это про очень долгую поддержку большой командой, что чаще всего есть только в продуктовой разработке. В небольшом проекте использовать многомодульность это оверхед.

Про плюсы которые несет многомодульность, я думаю вы уже слышали. Я лишь напомню вам про минусы:
👉 Первый и самый основной это DI. Какую бы либу вы не использовали, придется как-то изворачиваться, чтобы заставить ее работать с многомодульностью. 
👉 Второй это навигация. Из-за того, что ваш модуль ничего не знает об остальных придется делать абстракции поверх навигации, а это куча кода которого можно не делать когда все в одном модуле.

Подводя некоторый итог, если у вас небольшая команда или стартап не занимайтесь ерундой, вы больше времени потратите на болерплейт. Многомодульность нужна только в двух случаях. Первый я уже описал выше. Второй это когда вам нужно шарить часть функционала куда-то еще. В этом случае без выделения в отдельные модули никак.
👍38🥰2
Фича команды vs Отдельные команды(Хабы, Гильдии, Вертикали т.д)
🔥6
{1/2} Это мой первый пост, скорее про управление, нежели про технологию. Ну мы же все хотим когда-то вырасти в крутых лидов, поэтому иногда стоит и в этой теме что-то поизучать. Я дилетант в вопросах управления, поэтому воспринимайте все со здравым скептицизмом.

Недавно у меня с одним из разработчиком возник спор. Его очень сильно удивило, что у нас к компании практикуется создание кроссфункциональных команд. Самый основной вопрос был, зачем вообще объединять в одну команду Backend, Android, iOS, Frontend, QA и т.д? 

Есть же старое доброе разделение, команда Android отдельно, команда Backend отдельно и т.д. Даже если так подумать, объединение в одну команду Frontend и Backend имеет смысл. Однако в чем прикол объединять мобильные команды с фронтом, ведь у них принципиально разное поведение, дизайн и т.д.

Меня этот вопрос поставил в ступор. Для меня это была настолько очевидная вещь, что я не сразу нашел аргументы в ее защиту. Однако аргументы есть, и сейчас я расскажу почему в большом продукте отдельные команды это боль и единственная рабочая модель это команды по фичам.

Представьте, мы начинаем какой-то продукт. Когда мы его начинаем у нас совсем небольшая команда, допустим 10 человек. У нас все хорошо и никаких проблем нет. Тут не идет речи ни о каком разделении на фича команды. Вы и так все друг друга знаете, проект маленький и при возникновении проблемы, она быстро решается. 

Однако что делать, если вас только в Android команде 40 человек. А если сложить всех то на проекте человек 300. Возникают дико страшные издержки на коммуникацию. В таком случае, если команды разработки Android, iOS, Backend и т.д будут работать отдельно, вы не сможете координировать команды. Грубо говоря, если нет разделения по фича командам, значит все разработчики занимаются всем проектом в целом. Следовательно менеджеры будут кидать задачи в зависимости от загрузки разработчиков. 

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

Допустим Backend сделал эту задачу быстрее всех. Напомню, разрабы работают над всем проектом. Далее Android и iOS сделали задачу примерно одновременно. Однако при работе с фронтом, оказывается, что из-за специфики работы, нужно переделать запрос на стороне Backend. Только вот незадача, Backend разрабы которые работали над этой задаче, сейчас уже делают другие бизнес задачи, ну ооочень срочные. Ведь нужно загрузить разрабов, а то чего они ничего не делают. 

Frontend, не может продолжить работу над задачей, потому как нет свободных разрабов на стороне Backend. Android и iOS не могут релизиться, так как мы не можем выкатить фичу без Frontend. Поэтому Frontend начинает делать другие задачи. Когда Backend освобождается спустя кучу времени, у нас уже Frontend заняты другой более важной задачей. А когда начинается тестирование, ух…

Смекаете к чему я клоню? Из-за такого хаоса скорость разработки казалось бы просто фичи может улететь в космос. Пример конечно утрированный, однако я сам был участником похожей тусовки. Фичу которую засчитывали делать 3 недели, делали 4 месяца.
👍26
2025/07/09 19:11:33
Back to Top
HTML Embed Code: