Next Level Dev
619 subscribers
32 photos
2 videos
60 links
Делюсь опытом с ИИ, менторю начинающих android-разрабов

Roadmap для начинающих андроид-разрабов в личке @ilia_a_popov
Там же запись на менторство и консультации

О менторстве: https://androidmentor.ru
Download Telegram
Ответ на вопрос подписчика

Интересно общее мнение по поводу compose)😁
стоит ли например делать сейчас акцент\изучать compose

#android #вопросы
❤4👍2😁2
Убеди его сказать "Да"

Для доступа к опасным действиям в android-системе, например выходу приложения в интернет или записи файлов, нужно запросить у системы разрешение.

Есть два основных вида разрешений:

1️⃣ install-time – те, что можно просто выдать через манифест: например, разрешение на интернет.

2️⃣ dangerous (runtime) – те, о которых надо напрямую попросить пользователя. И вот о них поговорим поподробнее.

Запрос runtime разрешения представляет из себя показ соответствующего системного диалога. Это значит, что просто повесить listener на его кнопки и узнать, когда пользователь нажал «Да», «Нет» или «Нет и больше не спрашивать» нельзя. А узнать о том, что в итоге выбрал пользователь, можно только в соответствующем системном коллбэке.

Если вы просто покажете системный диалог с запросом разрешения и не будете слушать коллбэк, то при нажатии на «Да» диалог скроется и всё. А вам надо, чтобы пользователь прошёл дальше по флоу, который раньше был для него закрыт из-за отсутствия разрешения.

Прежде, чем показывать системный диалог, стоит внятно объяснять пользователю, зачем вам нужно разрешение, чтобы:

а) увеличить вероятность того, что он согласится дать разрешение и продолжит пользоваться вашим приложением

б) не выглядеть в глазах Play Store негодяями, запрашивающими разрешения с дурной целью

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

Если пользователь скажет "Да", то пускаете его дальше по флоу. Если "Нет" – не пускаете.
А вот если скажет "Нет, и больше не спрашивать" – вам надо продумать, что делать в таком случае. Ведь с этого момента вы больше не сможете запросить разрешение у системы, его можно будет выдать только вручную в настройках. Если без этого разрешения можно дальше пользоваться приложением – смиритесь, если нет – объясните пользователю, зачем ему теперь нужно идти в настройки, и что там нужно сделать.

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

#android #лайфхаки

@andrdevnotes | androidmentor.ru
👍7🔥4👏1😁1
Как Flow меня предал

Решил я недавно заменить синхронный код, длинными макаронами оплетающий приложение, на красивую удобную асинхронщину с Flow. Сказано – сделано.

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

interface Interactor {
fun onCameraMove(mapState: MapState)
}

interactor = parentFragment.checkInstance()

interactor?.onCameraMove(mapState)

interface MapCameraMovable {
fun onCameraMove(mapState: MapState)
}

override fun onCameraMove(mapState: MapState) {
(currentFragment as? MapCameraMovable)?.onCameraMove(mapState)
}


Стало: я сделал репозиторий с Flow, который асинхронно меняется в разных местах приложения, а в целевых местах он обзёрвится. Удобно и без бойлерплейта.

private val channel = Channel<MapState>(Channel.BUFFERED)
private val flow = channel.receiveAsFlow()

override fun observe(): Flow<MapState> = flow

override suspend fun once(): MapState? = flow.firstOrNull()

override suspend fun update(newMapState: MapState) = channel.send(newMapState)

Да вот только работало оно как-то странно: то правильно, то неправильно. Начал я копать:

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

fun <T> ReceiveChannel<T>.receiveAsFlow():

One element will be emitted to one collector only.

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

Мораль: слушаете Flow в нескольких местах – используйте MutableSharedFlow.

#android #лайфхаки #рефакторинг
👍8❤3
❇️ 5 простых лайфхаков для крутого кода ❇️

Поделился 5 простыми приёмами, которые помогут повысить качество вашего кода.

Приятного просмотра! 🚀

Мне будет приятно, если поддержите лайками и комментариями 👀

#android #видео #лайфхаки #рефакторинг

https://youtu.be/FJo4cRPJPMk?si=mHLUK_oL9_KcpLwV
🔥7🤩2👾2💯1🍓1
This media is not supported in your browser
VIEW IN TELEGRAM
🔥8❤2🥰1
❇️ 3 простых способа сделать разную вёрстку для планшетов и смартфонов ❇️

Отвечаю на вопрос подписчика: как правильно верстать под разные устройства.

Приятного просмотра! 🚀

Мне будет приятно, если поддержите лайками и комментариями 👀

https://youtu.be/VYQD9URfKVw?si=HsRtJDV1mGfXISHD

#android #видео #вопросы #лайфхаки

@andrdevnotes | androidmentor.ru
🔥10👍3🥰1
Практика over теория

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

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

✔️Поэтому со своими менти я следую подходу "Practice first", с самого начала и до конца объясняя всю теорию на примере написания конкретного приложения. Да, я, разумеется, даю необходимые ссылки на статьи и книги для изучения, но мы сразу применяем изученное, реализовывая очередную фичу в приложении. Так приложение постепенно обрастает "мясом", а в мозге менти всё складывается на нужные полочки и закрепляется.

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

Не понимаете смысла многострадальных inline и reified? Попробуйте написать экстеншн с дженериком
fun Context?.checkInstance(): T

который будет проверять, является ли этот Context типом T, если да – возвращать его, а если нет – кидать эксепшн.

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

#android #лайфхаки

@andrdevnotes | androidmentor.ru
Please open Telegram to view this post
VIEW IN TELEGRAM
💯5👍3❤1😍1
А какие вопросы по теории/практике в коде сейчас вас волнуют?

Над какой проблемой в коде сейчас больше всего паритесь?

Пишите в комментариях, разберем 👇🏻

Ответ на самую частую проблему запишу в видео 👀

#android #вопросы
🔥1
Ответ на вопрос подписчика

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

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

Но если и для избранного и для фильтрации и тд использовать только одну ViewModel, не нарушает ли это single responsibility principle?


Ответ: Single responsibility principle – принцип единственной ответственности. Чтобы ему следовать, нам надо сначала понять, а какая ответственность у вьюмодели и репозитория?

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

Ответственность репозитория: это управление источниками данных, их обработка и предоставление наружу в обработанном виде. Репозиторий ходит в сеть, кэширует, достаёт из кэша, маппит сетевые модели в доменные, обрабатывает ошибки, при необходимости маппит их в бизнесовые ошибки. Репозиторий отвечает за какой-то определённый кусок логики, например UserRepository или ImagesRepository.

Ответственность вьюмодели: вьюмодель – это прослойка между бизнес-логикой и определённым экраном. То есть вьюмодель не к фичам привязана, а к экрану. Если у вас на экране есть и добавление в избранное и фильтрация, значит и во вьюмодели это будет. Но вьюмодель делает это не сама, она обращается в вашем случае к конкретному репозиторию и получает из него результат. А репозитории вы уже проектируете как вам удобно: Например FavoritesRepository и FilterRepository, если хотите эту логику отделить. Или можете сделать один ImagesRepository, если не хотите отделять.

И запомните: однажды вам захочется сделать печально известную SharedViewModel и пошерить её между несколькими экранами. Это приведёт вас на путь, наполненный запутанным и нерасплетаемым спагетти-кодом, куче костылей и конфликтов. Не делайте так. Один экран – одна вьюмодель.

#android #архитектура #вопросы

Запись на менторство:
@andrdevnotes | androidmentor.ru
👍9🔥4💯2❤1
Привет, друзья!

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

Формат: вы до 19 мая присылаете мне ссылку на репозиторий со своим проектом. Я из них отбираю 5 проектов на разбор.

И я с 20 до 31 мая провожу один или больше стримов (в зависимости от объёма проектов) с подробным разбором и советами по рефакторингу.

Если у вас получится присутствовать на стриме – отлично, сможем лично пообщаться по проекту. Нет – посмотрите в записи.

Стоимость: 1990р за 1 проект.

Как вам идея?
👍4❤3🔥2
Офигеваю от того, как быстро вы отвечаете 🙂

Раз желающих много, проведу несколько стримов. Один стрим = 3-5 проектов
Проекты кидайте в личку: @ilia_a_popov

Первый стрим будет 21-го мая в 19-00
Привет, друзья!

Помимо долгосрочного менторства на 1, 2, 3 месяца, о котором можно подробнее почитать на androidmentor.ru, я также даю почасовые консультации под конкретный запрос.

Один из вариантов – тренировочное собеседование: либо основное техническое, по Java / Kotlin / Android, либо алгоритмическая секция с лайвкодингом на leetcode.com.

На скриншотах отзыв о последних двух моих собеседованиях.

Если хотите тоже попробовать свои силы – записывайтесь на индивидуальную консультацию @ilia_a_popov

#android #собеседование #консультация

@andrdevnotes | androidmentor.ru
👍6❤1🔥1👀1
Привет, друзья!

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

@andrdevnotes | androidmentor.ru
🔥3👍1👌1
Next Level Dev pinned Deleted message
Что делать, если всё пошло не по плану

Жизнь разраба – это не всегда модные кафешки, макбуки и смешные мемчики. Иногда разраб обсирается, причём по полной программе. Но крут не тот, кто не ошибается, а кто на ошибках учится.

Я сегодня планировал провести стрим с разбором проектов подписчиков. Было 14 желающих, но проект прислал в итоге только 1 человек. Супер-респект ему за смелость, он получит от меня бесплатную консультацию по его проекту в подарок.

Это косяк номер раз 🙂

Не беда, думаю я, сделаю стрим с лайвкодингом. Покажу, как с нуля написать проект, подключить сервисы, сделать всё по красоте. Новичкам точно будет полезно. Но не тут-то было :)) Ютуб решил проверить мой канал прежде, чем давать возможность проводить трансляции. Поэтому я вынужден перенести стрим на 28 мая.

Это косяк номер 2.

Но этот канал я заводил не для красивых постов, а для того, чтобы писать всё, как есть.

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

Плохо ли это?

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

Такие дела, друзья.

Ну что, проводим стрим с лайвкодингом в следующий вторник?

Ставь + в комментариях, если интересно.

#мотивация
👍16🔥6👏1
LiveData vs Flow: одноразовые ивенты и стейты

Начинающие разработчики часто путаются между LiveData и Flow. Для каких случаев подходит одно, а для каких – другое?
👀2🐳1
LiveData позволяет нам держать во вьюмодели стейт, который мы можем наблюдать из вьюхи. Стейт – это некая динамическая переменная, отвечающая за состояние какой-то сущности. Например – LiveData<List<Item>>, в которую будет приходить список, который мы хотим отобразить во вьюхе. Вьюха наблюдает LiveData и реагирует на каждое изменение этого списка, всегда отображая актуальные данные.

ViewModel:
private val _items: MutableLiveData<List<Item>> = MutableLiveData()
val items: LiveData<List<Item>> = _items

View:
viewModel.items.observe(viewLifecycleOwner) {
adapter.submitList(it)
}


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

Недостаток LiveData в том, что это класс из android-пакета, и использовать его, скажем, в domain-слое по клину не очень-то хорошо, особенно если есть прицел на мультиплатформенность.

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

ViewModel:
private val navigationEvents = MutableSharedFlow<NavigationEvent>(replay = 1)

View:
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.navigationEvents.collect {...}
}
}


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

К чему может привести неверное использование LiveData: если пытаться её использовать для, скажем, навигационных событий (показать / скрыть диалог или перейти на экран), сначала может показаться, что всё работает, но потом вы поймёте, что при смене конфигурации или возврате на экран мы будем получать старые данные (поскольку LiveData держит стейт), или ловить другие странные баги.

Поэтому запомните: нужны одноразовые события – используйте для них Flow. А LiveData – для простых стейтов, напрямую связанных с UI.

#лайфхаки #android

Другие полезные посты

Обучение android-разработке:
@andrdevnotes | androidmentor.ru
👍6🔥4❤1