Dev Easy Notes @android_easy_notes Channel on Telegram

Dev Easy Notes

@android_easy_notes


Лучший underground канал про разработку, JVM и computer science с глупыми шутками и несмешными каламбурами.

По сотрудничеству писать @haroncode

Dev Easy Notes (Russian)

Dev Easy Notes - это лучший underground канал про разработку, JVM и computer science с глупыми шутками и несмешными каламбурами. Если вы увлечены программированием, интересуетесь современными технологиями и хотите быть в курсе последних новостей в мире IT, то этот канал для вас. Здесь вы найдете полезные заметки, советы, ссылки на обучающие материалы и многое другое. Канал создан для тех, кто стремится к профессиональному росту в сфере разработки программного обеспечения. Если вы хотите делиться своими знаниями, общаться с единомышленниками и расширять свой кругозор, то присоединяйтесь к нам уже сегодня! Для сотрудничества пишите по контакту @haroncode.

Dev Easy Notes

21 Nov, 10:13


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

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

Dev Easy Notes

20 Nov, 10:28


Смотрите под ноги, дамы и господа, ибо я собираюсь уронить немного мудрости. Когда-то давным-давно я проходил курс по тестированию. Там говорилось о 7 основных принципах тестирования. Один из них называется "Скопление ошибок", который гласит: "Ошибки часто концентрируются в небольшом числе модулей".

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

Однако на практике я постоянно на это натыкаюсь, вот сука, почти каждый раз. Ты делаешь какую-то фичу, в ней обнаруживается баг. Ты лезешь его чинить и по пути обнаруживаешь ещё 3 бага, а если не обнаруживаешь, тебе эти 3-4 бага прилетят от QA в ближайшее время. Ну бывало же у вас такое?

Короче, в чём мудрость: если вам прилетел баг на какую-то часть системы, приглядитесь к ней тщательнее, скорее всего, там ещё бага 3-4 рядом.

Dev Easy Notes

18 Nov, 06:38


Недавно на работе возник разговор о Double check locking. Напомню если вдруг забыли. Вот представьте у вас тяжелый объект какой-то, который при создании требует кучу или памяти или времени, и вы не хотите его создавать сразу, а лениво когда понадобится. Мы можем конечно просто взять и вынести его в функцию, и потом вызвать в нужный момент:


private var heavy: HeavyObject? = null

fun get():HeabyObject {
if( heavy == null ){
heavy = HeavyObject()
}
return heavy
}



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


private var heavy: HeavyObject? = null

fun get():HeabyObject = synchronized { … }


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

Чтобы решить эту проблему, придумали подход называемый Double check locking, который используется в Dagger кстати. Суть проста, давай захватывать Mutex только если мы видим что объект еще не проинициализирован.


@Volatile
private var heavy: HeavyObject? = null

fun get(): HeavyObject {
var result: HeavyObject? = heavy
if (result == null) {
synchronized(lock) {
result = heavy
if (result == null) {
result = HeavyObject()
heavy = result
}
}
}
return result!!
}


Ну понимаете да, сначала чекаем на null, затем захватываем Mutex и еще раз чекаем, ведь кто-то мог захватить Mutex раньше и уже проинициализировать поле. Поэтому и double check.

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

Есть у JVM одна интересная особенность, все static блоки выполняются thread safe, т.е JVM их автоматически синхронизирует потому как важно гарантировать что этот блок вызовется лишь один раз. Еще фишка static блоков в том, что они вызываются только в момент обращения к классу. Поэтому мы можем использовать эту систему, чтобы сделать ленивую инициализацию:


private object FieldHolder {

val field: HeavyObject = createObject()

private fun createObject(): HeavyObject = HeavyObject()
}

fun get(): HeavyObject {
return FieldHolder.field
}


Не особо верится, что тут действительно ленивая инициализация или что оно thread safe. Ради эксперимента попробуйте сами это проверить)

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

Однако в своем коде, если хотите выебнуться на МРе можете такое попробовать. Только потом не плачьте, что с вами на обед не хотят идти. Один дурачок проверял, не будут раскрывать его личность....

Dev Easy Notes

08 Nov, 10:02


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

Dev Easy Notes

08 Nov, 09:41


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

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

Итак, набираем воздух в грудь, как же полностью избавится от страха перед собесами?

Никак нахуй!

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

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

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

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

Dev Easy Notes

06 Nov, 09:20


Сейчас делаю на работе бота, по сути небольшой бекенд и мне нужно было валидировать модели в запросах. Весьма дефолтная задача, приходит объект, он должен удовлетворять каким-то правилам. Если какие-то поля заполнены неверно, нужно фронту отдать список полей в нужном формате с припиской почему именно поле заполнено неверно. Задача максимально базовая, и в Spring решается за пару аннотаций. Однако так уж вышло что у меня используется ktor и поэтому такое приходится делать самому.

Есть два варианта как это сделать: пишем валидацию и генерацию текста ошибок вручную или используем уже какую-то либу для kotlin, которая это умеет делать.
Разумеется я рот топтал делать это вручную. Все либы для валидации которые я нашел, работают по одному и тому же принципу. Получаем KProperty, который позволяет получить как данные, так и название поля, что помогает на месте выдать список ошибок. А дальше через dsl прописываем условия, которым поле должно удовлетворять, и текст ошибки если не удовлетворяет.

Я решил затащить вот такую. В целом довольно удобный API, можно дописывать свои проверки, короче мою задачу решает. Но…, разумеется я бы не стал про это писать, если бы не было "но". Есть две вещи которые меня страшно веселят. 

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

Второе, в этой либе есть ksp. И вот с одной стороны ты можешь подумать, ну ведь можно же представить чтобы такую либу использовать в мобилке и там, чтобы без рефлексии было? Представить можно, однако знаете для чего тут нужен ksp? Исключительно для того, чтобы обернуть KProperty в один конкретный метод. Другим словами вместо:
validatableOf(Person::name).isNotEmpty()

можно было написать:
name.isNotEmpty()

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

Dev Easy Notes

04 Nov, 10:55


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

Итак, вершина инженерной мысли, 9 симфония в мире Java Backend, покровитель всего интерпрайза – Spring.

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

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

Component. Как отдельного класса его нет. Тут можно представить что создается компонент на каждый класс, в котором ты используешь DI. Поставил аннотацию @Component и все, теперь внутри класса можешь инжектить все что тебе вдумается и главное как тебе вдумается. Spring умеет инжектить и через конструктор, и через приватное поле, и через setter и даже через твою ма…

Module. Реализуются похожим на Dagger подходом. Делаем класс, реализуем методы для создания зависимостей, проставляем каждому методу аннотацию @Bean (не спрашивайте почему Bean, интерпрайзные приколы). Далее классу проставляем аннотацию @Configuration и все. На практике такие модули создают редко и только для всяких конфигураций. Всякие Repository и Interactor у нас являются @Component, поэтому их Spring за тебя сам создаст и запихает куда нужно.

Scope. Опять-таки аннотация @Scope. Можно сделать чтобы зависимость жила пока не умрет все приложение, можно сделать чтобы зависимость жила пока живет сессия или вообще сделать scope на уровне запроса. Ну и также этой же аннотацией мы проставляем Singleton или Prototype.

Ну и касательно зависимостей в явном виде как это сделано в Dagger их нет, тут все аналогично тому, как это работает в koin. Но и на бэке гораздо реже происходит ситуация, когда делают много модулей, там, как правило, разъезжаются на микросервисы, когда становится много команд.

P.S для матерых бекендеров. Да я знаю что есть еще @Service и @Repository которые работают слегка по другому, но давайте не усложнять сейчас)

Dev Easy Notes

27 Oct, 12:53


Ладно а теперь на чистоту, вы мне много чего накидали за воротник по поводу koin. Многие пишут, что вот у нас 100 экранов и вообще никаких проблем мы не испытываем. Хорошо, допустим, вам все нравится, в прописывании кучи get, поиском ответа на вопрос, а откуда же мне тут приходит зависимость и прописывании изолированных контекстов вы не видите ничего плохого.

При этом вы точно уверены, что у вас достаточно большой проект сейчас? Я сейчас работаю на проекте, где 40+ Android разрабов, более 1000 gradle модулей, а количество экранов я даже посчитать не смогу, кажется уже бесконечность. В проекте такого уровня использовать Koin это страшное преступление.

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

Dev Easy Notes

27 Oct, 12:50


Ой да бросьте, смешно же. Ладно, ок я понял, вам нравится koin, говоря откровенно он и мне нравится, но только в проектах на бэке, и которые я делаю в соло. Однако зачем же вы, понимаете мои слова так буквально? Под 5-ю экранами, я не подразумевал именно 5 экранов, более того если у вас 5 экранов, вам вообще DI нахер не нужен. Я разумеется подразумевал 10 экранов, вот если больше 10 то koin уже нельзя, так своим коллегам и передайте, что ноунейм с канала в телеге, запретил в таком случае использовать koin.

Dev Easy Notes

25 Oct, 11:30


Далее koin. Когда в детстве разработчиков Koin подкидывали и забыли ловить… ладно объясню нормально.

Module. В нем создаем зависимости, аналогично Dagger. Далее эти модули нужно указать в инициализаторе Koin.

Component. Он так и называется KoinComponent, позволяет использовать функции для получения зависимостей. Ну на самом деле он просто ищет глобальный контекст Koin и через него уже получает доступ ко всем загруженным модулям. Помимо этого в модуле для Android уже реализованы расширения для использования inject и get во всех стандартных компонентах.

Dependencies. А вот с этим в Koin все довольно забавно. Эта собака же сама все хранит в глобальном контексте. Поэтому работает это так. Вот в app модуле приложения вы прописали какие модули Koin нужно загрузить т.е сразу все. И так как контекст то у Koin один, что значит все теперь все модули доступны везде. Что означает, вы в фиче модуле, в модуле Koin можете сразу использовать зависимости, которые вы например определили в app модуле. Минус в том, что вы можете случайно получить зависимость с другого фича модуля и отслеживать это придется вручную.

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

Scope. В Koin можно прям явно указать что объекты создаются на время работы конкретного экрана, т.е они привязаны в Activity или фрагменту и уничтожаются при выходе с этого экрана. Тут все более явно чем в Dagger.

Dev Easy Notes

24 Oct, 11:24


Первый у нас это Dagger, он прям идеально кладется на модель:

Module – в нем указываем как именно нам создавать наши зависимости. Можно делать вручную, можно через bind, тут не особо важно.

Component – интерфейс, чтобы получать зависимости из Module. У Dagger Component есть возможность указать в какой класс нам нужно все заинжектить и Dagger сам все сделает, без необходимости доставать все вручную. Однако при желании, можно вручную ходить в Component и вытаскивать что нам нужно.

Dependencies – удивительно, но не все знают что при описании Component вы можете указать класс интерфейса, откуда брать внешние зависимости. Причем вы указываете просто интерфейс. Этим интерфейсом может быть как и другой Component Dagger, так и просто обычный класс, который вы руками реализовали. Далее этот класс (или Component) можно подсунуть при создании Component где вы указали Dependencies.

Что по scope у Dagger? В рамках компонента все понятно, есть Provider, есть Singleton. Можно конечно еще создать свою аннотацию, которая по сути будет Singleton, но об этом в другой раз.

Касательно Scope для компонента, все довольно просто. Вы его контролируете ручками. Создали компонент и сохранили его в Application, все он будет жить пока не умрет приложение. Создали компонент и сохранили его в Activity, он будет жить пока не умрет Activity (т.е даже при повороте экрана).

Dev Easy Notes

24 Oct, 11:11


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

👉 Dagger 2
👉 Spring
👉 Koin
👉 Custom

Первый это база для Android разработки, второй используется только на бэке, а Koin он, ну вы знаете, Koin… Ну и ручной DI тоже рассмотрим.

Я постараюсь показать как, именно каждый из этих DI кладется на модель, которую я предложил.

Dev Easy Notes

22 Oct, 10:05


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

Dev Easy Notes

05 Oct, 12:21


Видели такое?

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

В целом я даже могу предвидеть какую причину они укажут для этой эксгумации. Теперь вы можете свой легаси проект перевести на multiplatform еще быстрее, ведь Async Task теперь можно использовать и в iOS.

Возникает вопрос, что это за проекты такие, которые в 24 году еще используют Async Task? Если же такие еще есть, и за все это время у них не хватило ресурсов перевести это легаси на новые подходы, как вы думаете насколько приоритетной задачей для них будет перевести все на KMP?

Dev Easy Notes

01 Oct, 09:52


Как-то в разнобой посты делаю, однако хотел закончить серию про доку. В частности как правильно ее делать?

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

И вот очередная мудрость от меня, как писать крутую доку. Топ 3 фишки, которые вам помогут.
👉 После написания, представляете что вы обделены разумом (в моем случае это представить очень легко) и по свой доке пытаетесь что-то сделать, не отходя от того, что вы написали. Получилось? Если да, то вы либо себя наебали, либо с первого раза сделали крутую доку. Если не получилось, дописываете и повторяете процедуру.

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

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

Dev Easy Notes

22 Sep, 11:12


Я правильно понимаю, что я стараюсь, публикую посты про DI, про доку и на каждый полезный пост от меня отписывается человек по 5?

При этом стоит вбросить какую-то дичь с пьяну про Сыктывкар, на меня подписалось 15 человек. Бля, я кажется до сих пор не понимаю правил этой игры….

Dev Easy Notes

19 Sep, 18:21


Ребята, немного офтоп. А у меня из подписчиков есть кто-нибудь из Сыктывкара? Я просто не верю что такой город существует)

Dev Easy Notes

17 Sep, 08:24


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

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

Dev Easy Notes

16 Sep, 11:09


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

При этом, сейчас, несмотря на то, что я давно уже не трогал dagger, я смогу не заглядывая в доку, рассказать про то, как он работает. Все кто читает меня давно знают, что я в своем канале продвигаю одну мысль: основные концепции в разработке практически не меняются. Меняются только API и всякие разные тонкости.

Поэтому я решил рассказать вам, про свою ментальную модель, которая позволит вам без каких либо проблем разобраться в любом DI, не важно будет ли это Dagger или самописный.
Заинтересовались? Тогда погнали.

В любом DI есть четыре основные сущности, которые нужно запомнить:

👉 Module – класс или функция в которой зависимости генерятся
👉 Component – интерфейс, который позволяет получить нужные зависимости
👉 Dependencies – зависимости которые будут использоваться в Module, получаемые извне, например из другого модуля
👉 Scope – опциональная штука, суть которой определить время жизни зависимостей. Причем не нужно тут завязывать на то, что Scope обязательно привязан с Activity и т.д вы при желании можете сделать его тупо по таймеру, что например он живет 5 минут.

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