Android Live 🤖 – Telegram
Android Live 🤖
5.28K subscribers
52 photos
1 video
800 links
Самые свежие новости, новинки и тренды Android от практикующего разработчика.


Автор: @al_gorshkov,
Чат: @android_live_chat
Личный блог: @al_gorshkov_blog

Рекламу не размещаю
Download Telegram
​​Паттерны проектирования в Android разработке
#design #patterns #beginners

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

Если вы хотите связать существующие паттерны с Android-разработкой, то есть отличная статья, которая разбирает основные паттерны и описывает примеры, которые есть в Android. Вот некоторые из шаблонов: Builder, DI, Singleton, Factory, Adapter, Facade, Observer и многие другие.

Ну и обильные примеры кода также весьма радуют. Ссылка на статью тут.
​​Google IO 2021
#conference

На этой неделе Google анонсировал даты конференции для Android-разработчиков — Google IO 2021. В этом году она пройдёт с 18—20 мая только онлайн, без возможности оффлайн участия.

Пока нет деталей о том, чего ждать на этой конфереции. Судя по всему, это будет анонс beta-версии Android 12 с более детальным рассказом о том, что он нам принесёт, релизом новых библиотек и подходов.

Уверен, что будет детально рассказано про Jetpack Compose и его статус, возможно он уже будет production ready.

Тут можно решить небольшой ребус, а тут зарегистрироваться на само мероприятие, оно полностью бесплатное.

Ну и вы узнаете обо всех новинках конфереции первыми, так как подписаны на Android Live, так что следите за обновлениями. 😉

А может быть у вас есть идеи особых меропрятий для канала Android Live, которые посвящены этой конференции?

Можете поделиться в комментариях.
​​Сервисы для аналитики приложений
#library

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

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

Весь список с детальным объяснением можно найти в этой статье, а мы рассмотрим несколько из описанных.

🔸продуктовая аналитика — нужна для получения информации о том, что конкретно делает пользователь в приложении. Сервисы — App Metrica и Firebase Analytics, Amplitude, Mixpanel.

🔸отправка пуш-уведомлений — полезны, если вы настраиваете группы пользователей, которым хотите их отправить. Сервисы — Firebase Cloud Messaging, AWS SNS, OneSignal.

🔸подключение платежей и подписок — очевидная и простая на первый взгляд вещь, но довольно непростая в реализации. Сервисы — Adapty, AppHud, RevenueCat.

🔸аналитика падений — наиболее близкая разработчикам вещь. Сервисы — AppMetrica, Firebase Crashlytics, Sentry.

Помните, что многие из описанных выше сервисов — платные, но многие из них дают бесплатную функциональность для маленьких продуктов.
​​Dependency Injection vs Service Locator
#patterns

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

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

Хорошее определение этих паттернов нашёл в этой статье.

🟢 если описать DI одним словом, то идеально подходит слово «отдавать». И в самом деле, при помощи DI мы просто даём нужные объекты другому объекту. В примере ниже классу House нужны объекты Door и Window, которые мы передаём ему в конструктор.

val window = Window()
val door = Door()
val house = House(window, door)


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

🔵 если мы описываем Service Locator одним словом, то идеально подходит слово «взять». Так, и есть: у нас есть какой-то класс (локатор, фабрика) у которого мы берём объекты, которые нужны нашему классу. В примере ниже, мы возьмём объект класса House напрямую из какого-то локатора и будем использовать его дальше.

val house = serviceLocator.get(House::class)

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

Современные библиотеки для внедрения зависимостей, такие как Dagger, Hilt, Koin, используют оба этих подхода в связке, хотя это и не всегда очевидно на первый взгляд. Но как мне кажется — это здорово, ведь каждый из них имеет свои плюсы, а подобное сосуществование уменьшает число недостатков.
​​Kotlin Flows — шпаргалка
#flow

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

Вот вам ещё одна небольшая заметка про Flow, где автор собрал в одну таблицу краткую информацию о них и о всех существующих типах Flow: в чём их разница, какие есть эквиваленты в RxJava, ссылки на документацию, примеры кода, где лучше применить их в Android.

Для тех, кто хочет шпаргалку сразу в pdf — вот ссылка, а сама статья с описание тут.
​​Как правильно передавать данные между Fragments?
#jetpack #fragment

Для передачи данных между Fragments есть несколько способов:
• использовать интерфейсы и callbacks;
• использовать Shared ViewModel;
• использовать setTargetFragment, правда он сейчас deprecated.

Но есть ещё один способ, который сейчас является самым удобным и основным. Начиная с версии Fragments 1.3.0-alpha04, FragmentManager имплементит FragmentResultOwner.

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

Важно следить за уникальным использованием ключей, а также есть небольшие особенности при работе с childFragmentManager. Больше деталей и примеров кода можно найти тут.
​​Планируем задачи с WorkManager
#workmanager #jetpack #beginners

WorkManager — довольно важный компонент из Jetpack, который позволяет планировать задачи вне зависимости от того было ли закрыто ваше приложение или перезагружено устройство.
У него есть несколько преимуществ в сравнении с предшественниками: например, работа при определённом заряде батареи или Интернета, гибкие настройки для повторного запуска, интеграция с Coroutines и RxJava.

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

Ссылка на статью тут.

Были ли у вас проблемы с использованием WorkManager?
​​Лайфхаки для Firebase Remote Config
#firebase

Firebase Remote Config — это удобный сервис, который позволяет отправлять некий набор параметров через Firebase без участия вашего собственного сервера. Это удобно, ведь могут быть ситуации, когда у вас вообще нет сервера (например, в pet-проекте) или вам не просто добавить новую функциональность на ваш сервер.

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

0️⃣Не забывайте о значениях по умолчанию в приложении. Об этом мало кто помнит, но вы можете установить значения по умолчанию для сервиса до тех пор, пока сервис не получить свой первый instance с Firebase. Это может быть полезно в случае каких-то проблем при получении данных. Сделать это можно двумя способами. Первый — это определение значений по умолчанию при получении определённой переменной, а второй — определить в самом config:
Firebase.remoteConfig.apply {
setDefaultsAsync(mapOf(
"key" to "default_value"
))
}


1️⃣Оберните получение данных в WorkManager. При использовании методов fetch или fetchAndActivate, вы можете получить исключение, например когда пользователь не имеет подключения к Интернет. Правильнее всего попробовать получить эти данные ещё раз, когда будет восстановлено соединение. Можно испльзовать Worker, где в конфигурации настроить запуск только при наличии соединения: setRequiredNetworkType(NetworkType.CONNECTED).

2️⃣Свяжите Remote Config с Cloud Functions. Довольно спорное решение, но оно может быть полезно, если вы хотите доставить обновления Config как можно быстрее. Идея в том, что вы отправляете пуш-уведомление пользователям при изменении Remote Config, делая текущее состояние данных неактуальными и заставляя FirebaseRemoteConfig получить данные как можно быстрее при следующем запуске приложения.
​​Автомиграции в Room
#room #jetpack

В версии Room 2.4.0-alpha01 добавили новую функциональность, которая значительно упрощает миграцию с одной версии базы данных на другую — автомиграции. До этой версии у вас было два пути при изменении схемы базы:
• писать миграцию вручную;
• заполнять базу данных заново, удаляя предыдущую версию базы.

Теперь появился гораздо более интересный способ. При повышении версии базы данных, вы можете указать параметр autoMigrations и добавить AutoMigration (from = 1, to = 2). Это всё, вы автоматически получите миграцию на новую версию. Более того, есть возможность комбинировать автомиграции и обычные миграции.

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

Кроме этого, добавили AutoMigrationSpec, которая может помочь Room сделать автомиграцию, если он не справляется сам. Там есть несколько аннотаций: DeleteTable, RenameTable, DeleteColumn, RenameColumn. В случае, если Room сам не сможет сделать миграцию, то вы получите exception. Это и будет являться признаком того, что надо использовать эти аннотации.

На мой взгляд — это отличная новость, которая избавит нас от написания большого числа лишнего кода в проекте. С нетерпением жду beta-версии.
​​Модуль навигации в Jetpack Compose
#jetpack #compose

Нашёл отличную статью, где автор приводит пример навигации при помощи Navigation Compose в приложении.

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

Статья получилась объёмной, но описанный подход заслуживает внимания. Ну и ссылка на проект, который там упоминается. В нём используется довольно свежий стек технологий, можно посмотреть, как ведут себя сырые библиотеки на готовом проекте.
​​Android Runtime: как работают Dalvik и ART
#theory

Android Runtime — одна из самых ключевых вещей в Android. Большинство слышали такие аббревиатуры как Dalvik, ART, JIT или AOT. Но далеко не все понимают, что они значат и как вообще работает среда выполнения Android.

В статье или видео можно найти детальную информацию об этом. Для начала рассматривается вообще понятие среды выполнения, а далее сравниваются все современные (и не очень) среды.

Подобная информация полезна для общего понимания того, из чего состоит приложение и как оно работает, ну и будет полезна при прохождении собеседований.
​​Оптимизация кастомных View: Live-coding сессия — Евгений Зубков
#youtube

Оптимизация — это очень важная часть современного приложения. И хотя про это много говорят и написана куча статей, даже у опытных разработчиков оптимизация может отнять много времени и сил. Что уж говорить о тех, кто совсем недавно пришёл в разработку.

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

Сделаем это с нашим экспертом, Евгением Зубковым из компании Revolut. Евгений — разработчик с 10-летним опытом разработки под Android и автор статей.

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

Стрим состоится 5 мая в 18:30 по МСК на YouTube-канале Android Live, ссылка тут. И конечно предлагаю вам подписаться на YouTube-канал, чтобы вы могли получать уведомления о новых стримах и трансляциях. ✌🏻
​​Опыт перехода с Mac на Ubuntu
#offtop

Любопытная статья, которая описывает опыт Android-разработчика при переходе с Macbook на кастомный компьютер с Ubuntu.

Основным моментом из-за которого он запланировал переход — неготовность платить раз в несколько лет за покупку нового Macbook. Конфигурация его компьютера здесь.

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

В остальном — только плюсы:
• более шустрая работа git;
• более мощный терминал для работы;
• ускоренная сборка билдов;
• довольно низкая цена за итоговый компьютер.

Статья не изобилует различными сравнениями и бенчмарками, но личное мнение всегда интересно. Интересно, как будет сравниваться его компьютер с новыми чипами от Apple, ведь стабильной версии Android Studio вроде как нет (но есть alpha).

А был ли у вас опыт перехода с одной операционной системы на другой? Какие ощущения?. Будет интересно, если расскажите в комментариях.
​​Паттерн Builder в Kotlin
#patterns

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

Этот паттерн широко используется в различных библиотеках или подходах. Например, в Android одним из самых распространённых примеров является создание AlertDialog при помощи Builder.

Вот хорошая статья, которая рассказывает о правильном создании объектов при помощи Builder. Пару тезисов оттуда:

0️⃣ Как ни странно, при создании объекта через Builder важно верно определить конструктор, который будет принимать параметры без которых объект не может существовать. В примере с AlertDialog таким параметром будет Context.
Например, в прошлых версиях Android у нас была возможность сделать Notification, который не показывался бы системой, а также приложение не падало с исключением – пример неверно созданного Builder.

1️⃣ Для каждого поля необходимо выставить параметр по умолчанию, например null. При использовании шаблона Builder рекомендуется сделать конструктор private, чтобы ограничить создание объекта только для внутреннего Builder.

2️⃣ Важно сделать верификацию объектов. Автор дает сразу 3 примера, где можно верифицировать добавляемые объекты: сразу после использования метода, при вызове метода build() или уже внутри объекта.
Хорошей практикой в данном примере будет использование 2 и 3 подхода одновременно.

3️⃣ Для того, чтобы сделать свой Builder можно использовать Kotlin DSL, который идеально подходит для этого паттерна. Кроме того, не стоит забывать о именованных параметрах, особенно когда вы указываете переменные одного типа.

В целом, этот паттерн один из самых популярных, и широко применяется на практике. Используйте его, если у вас есть большое число конструкторов, и вы заметно улучшите читаемость вашего кода. ✌️
​​Релиз Koin 3.0.1
#koin #kmm

Совсем недавно вышла стабильная версия Koin — 3.0.1. Давайте кратко рассмотрим, что нового там добавилось:

🔸переход на mavenCentral вместо JCenter — это было ожидаемо, и теперь нужно поменять зависимости.
🔸поменялся состав зависимостей. Например, раньше были отдельные зависимости для Android: ViewModel, Scope. Теперь всё это объединили в единую зависимость Android. Кроме того, добавилась зависимость для Jetpack Compose, которая пока находится в нестабильной версии.
🔸стабильная поддержка KMM🎉. Наверное, это самая ожидаемая часть релиза: теперь можно подключать Koin в проекты KMM, что даёт нам ещё один крутой фреимворк для внедрения зависимостей. Проект с примером тут.
🔸правки в API, которые улучшают стабильность фреимворка и дают больше возможностей для улучшений.
🔸AndroidX теперь по умолчанию. Теперь нет поддержки support-библиотек, но я думаю, что вы уже давно мигрировали свои проекты на AndroidX.
🔸интеграция с Jetpack Compose — как уже сказал выше, зависимости выделены в отдельный модуль, но теперь также появилсь возможность использовать Koin в проектах с Jetpack Compose. Примеры приложений можно найти тут.
🔸переделали дизайн сайта с документацией. Ссылка осталась прежней.

Если нужно больше деталей — переходите на статью с анонсом релиза.
​​Hilt is stable!
#library

Hilt — это рекомендованное Google решение для DI. Со вчерашнего дня оно перешло в статус stable, а это значит, что теперь можно использовать его в production.

Hilt гораздо проще, чем Dagger, даёт возможность писать меньше boilerplate-кода и гораздо лучше интегрирован с Jetpack-библиотеками. Например, у неё есть интеграции с ViewModel, WorkManager, Navigation, и Compose.

Чуть больше информации об этой библиотеке можно взять тут, тут официальная документация, а тут пример приложения с этой библиотекой.

Интересно, будет ли Hilt поддерживать KMM в будущем? 🤔
​​Раздел безопасности в Google Play
#security #googleplay

Google сделали преанонс safety section в Google Play, который поможет пользователям понять, какую информацию собирает приложение, безопасны ли эти данные, а также дополнительную информацию о безопасности этого приложения.

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

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

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

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

А что думаете вы по этому поводу?
​​Операторы shareIn и stateIn
#kotlin #flow

Операторы позволяют конвертировать cold flow в hot flow: они могут передавать информацию, которая приходит от потока и транслировать её нескольким подписчикам.
Эти операторы используются, если требуется улучшить производительность, добавить буфер, если нет подписчиков, ну или добавить механизм кэширования.

Между shareIn и stateIn по сути, существует только одна разница: первый преобразует Flow в SharedFlow, а второй — в StateFlow.

Вот хорошая статья, которая детально описывает применение этих операторов на примерах.

Главное запомнить, что никогда не стоит использовать эти операторы при возвращении Flow из функции: в этом случае у вас будет создаваться новый SharedFlow или StateFlow при каждом вызове функции, и он будет оставаться в памяти до тех пор, пока не очистится Scope или GB не уберёт его при отсутствии ссылок. В любом случае, это поведение неверное.
​​Android Live на Boosty
#android_live

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

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

Наконец, на Android Live появились возможность поддержки канала рублём. Сделать это можно при помощи сервиса Boosty одноразовым или ежемесячным платежом.

Уже сейчас там есть цель и два уровня подписки. Думаю для начала этого достаточно, посмотрим, зайдёт ли эта возможность.

Ну и ответы на пару вопросов, которые могут возникнуть:
0️⃣ Будет ли в будущем контент распространяться за плату? Нет, такого точно не будет, канал останется бесплатным.

1️⃣ Зачем мне поддерживать канал? Поддерживая канал, вы даёте мне понимание того, что контент интересен. Плюс к этому, я смогу инвестировать эти средства в развитие канала: покупка рекламы, оборудования и т.д.

2️⃣ Можно ли доверять Boosty? В целом да. Я не заметил никаких скрытых платежей, сервис принадлежит большой компании и там есть возможность одноразового доната. Кроме того, комиссия за вывод средств одна из самых низких.

3️⃣ Можно ли как-то ещё помочь каналу? Материально пока что нет. Для меня это эксперимент, может быть в будущем такая возможность появится. Но если захотите скинуть кусочек биткоина на развитие — пишите😀.
Ну а если вы хотите помочь каналу не рублём, а делом — также пишите, сможем что-нибудь придумать.

Ссылка на Boosty тут.
​​ListAdapter для RecyclerView
#recyclerview

Почти все приложения так или иначе используют RecyclerView. И одной из самых важных частей в ней является Adapter.

По умолчанию используется RecyclerView.Adapter, где нам требуется переопределить методы для создания и заполнения ViewHolder и указать число элементов в списке.

После этого, важно сделать корректное обновление списка: при этом не стоит использовать стандартный метод notifyDataSetChanged(), который обновит полный список, а лучше применить DiffUtil, который корректно обновит только новые элементы.

Со временем адаптер может обрасти довольно большим количеством boilerplate code.
Для упрощения работы лучше использовать ListAdapter, который:
• требует для работы DiffUtil.ItemCallback;
• рассчитывает разницу между двумя списками в background-потоке;
• добавляет метод submitList(), который принимает на вход новый список, поэтому не нужно больше думать о сохранении списка внутри адаптера.

Чуть больше примеров использования этого класса можно найти тут.
​​Для чего нужны value классы?
#kotlin

Начиная с версии 1.5, в Kotlin появились value-классы. На первый взгляд, это новая функциональность, однако, по сути, она заменяет собой уже известные нам inline-классы, которые, в отличии от inline-функций не встраивались в код. Чтобы избежать этой путаницы, теперь их вывели в отдельную сущность и сделали value-классами.

Чтобы понять, для чего нам нужны эти классы, можно воспользоваться примером из статьи.

Допустим, у нас есть фукнция, которая на вход принимает параметр в виде duration для показа какого-то сообщения с задержкой:
fun showTooltip(message: String, duration: Long) { ... }

Сходу непонятно, передавать этот параметр в секундах или в миллисекундах. И даже если переименовать параметр в durationInMillis и добавить описание функции, всё равно есть риск допустить ошибку.

Хорошим решением здесь является создание обёртки, которая заставит явно указать единицы измерения. Например:
class Duration private constructor (
val millis: Long
) {
companion object {

fun millis(millis: Long) = Duration(millis)

fun seconds(seconds: Long) = Duration(seconds * 1000)
}
}


В этом случае мы спокойно можем модифицировать нашу функцию, чтобы принимать на вход Duration, а дальше создавать нужную нам задержку. Проблема в том, что каждый раз будет создаваться объект и тратиться лишняя память. Для таких случаев идеально подходят value-классы. Всё что нужно — это добавить ключевое слово value перед классом:
@JvmInline
value class Duration private constructor (
val millis: Long
) {
companion object {

fun millis(millis: Long) = Duration(millis)

fun seconds(seconds: Long) = Duration(seconds * 1000)
}
}


Взамен мы получим класс, который заменит примитив, и мы получим оптимизацию. Чуть больше почитать об этом можно в этой же статье.