Бессмертное приложение
Продолжим разбирать доклады с предыдущего Mobius. И сегодня поговорим о том, как пережить force stop и запустить приложение после его установки из стора без действий пользователя.
Мы знаем, что если пользователь принудительно остановит в настройках приложение, то без повторного его открытия все фоновые операции будут недоступны, в том числе и пуши. Но эту проблему можно частично решить, для этого нам понадобится
1. Регистрируем в манифесте provider с метадатой ContactDirectory. Тогда при открытии приложения "Контакты" ContactsProvider опросит всех, у кого есть эта метадата, соответственно поднимется наш провайдер и вызовется метод
2. Далее мы можем зарегистрировать свой
Но согласитесь, это же не бессмертие, а больше похоже на некромантию? Убиваем и воскрешаем процесс через какое-то время💀
Однако есть способ по-настоящему пережить force stop, правда работает он не гарантировано и ни в каких продакшн приложениях лучше его не использовать⚠️
Суть в том, что у системы есть 40 попыток, чтобы убить процесс с периодичностью 5мс, и если мы будем за это время создавать новые процессы, то система в теории просто сдастся. То есть процессы будут поднимать друг друга пока не пройдет достаточно времени, чтобы система оставила их в покое. Посмотреть на это безумие можно в репозитории.
Всем разработчикам долгоживущих приложений, а пользователям соболезнования по поводу батарейки в их смартфонах🫡
#Android #UnderTheHood
Продолжим разбирать доклады с предыдущего Mobius. И сегодня поговорим о том, как пережить force stop и запустить приложение после его установки из стора без действий пользователя.
Мы знаем, что если пользователь принудительно остановит в настройках приложение, то без повторного его открытия все фоновые операции будут недоступны, в том числе и пуши. Но эту проблему можно частично решить, для этого нам понадобится
ContactsProvider
и Account Synchronization
.1. Регистрируем в манифесте provider с метадатой ContactDirectory. Тогда при открытии приложения "Контакты" ContactsProvider опросит всех, у кого есть эта метадата, соответственно поднимется наш провайдер и вызовется метод
onCreate
у Application
даже без первичного запуска приложения.
<meta-data
android:name="android.content.ContactDirectory"
android:value="true" />
2. Далее мы можем зарегистрировать свой
SyncAdaper
для синхронизации аккаунтов, он может не иметь никакой логики, главное включить автоматический и периодичный синк. Тогда система сможет при синхронизации аккаунтов сама пробудить процесс даже после force stop!Но согласитесь, это же не бессмертие, а больше похоже на некромантию? Убиваем и воскрешаем процесс через какое-то время
Однако есть способ по-настоящему пережить force stop, правда работает он не гарантировано и ни в каких продакшн приложениях лучше его не использовать
Суть в том, что у системы есть 40 попыток, чтобы убить процесс с периодичностью 5мс, и если мы будем за это время создавать новые процессы, то система в теории просто сдастся. То есть процессы будут поднимать друг друга пока не пройдет достаточно времени, чтобы система оставила их в покое. Посмотреть на это безумие можно в репозитории.
Всем разработчикам долгоживущих приложений, а пользователям соболезнования по поводу батарейки в их смартфонах
#Android #UnderTheHood
Please open Telegram to view this post
VIEW IN TELEGRAM
Корутинные рецепты
Возможно вы уже видели набор полезных рецептов для Kotlin Coroutines от Marcin Moskala. Это набор утилитарных функций, которые помогают в повседневной разработке, позволяют значительно сократить код и сделать его лаконичнее.
🔹 Функция mapAsync позволяет элегантно сделать загрузку данных параллельной при работе с коллекциями
🔹 Функции retryWhen и retryBackoff позволяют сделать повторные запросы в случае ошибки, а вторая функция позволяет сделать ожидание экспоненциальным, чтобы увеличить вероятность успешного повтора
🔹 Функция raceOf позволяет запустить несколько suspend функций параллельно и получить результат той, что завершится быстрее остальных. На моей практике, это единственная функция, которая использует механизм select в корутинах.
🔹 Функция suspendLazy позволяет создать ленивое suspending свойство, которое не будет блочить поток, обращающийся к этому свойству
🔹 Ну и различные SharedDataSource и StateDataSource, которые нужны, чтобы пошарить один экземпляр Flow для нескольких подписчиков по переданному ключу.
📎 Помимо этих рецептов, также есть либа с полезными операторами для Flow, взятыми из Rx.
💡 Еще к этому списку можно было бы докинуть runCatchingCancellable, который не съедает CancellationException или функцию, противоположную raceOf, которая выберет самый медленный источник, ведь иногда нам нужно, наоборот, подзамедлить UI, чтобы он не мелькал и пользователь понимал, что происходит на экране.
Если вы знаете еще полезные экстеншены для корутин, делитесь ими в комментариях💬
#Coroutines
Возможно вы уже видели набор полезных рецептов для Kotlin Coroutines от Marcin Moskala. Это набор утилитарных функций, которые помогают в повседневной разработке, позволяют значительно сократить код и сделать его лаконичнее.
Если вы знаете еще полезные экстеншены для корутин, делитесь ими в комментариях
#Coroutines
Please open Telegram to view this post
VIEW IN TELEGRAM
👉 Какая IDE лучше?
👉 Чьи приложения качественней?
👉 У кого лучше дизайн?
👉 Где больше инноваций?
👉 Зачем натив, если есть кроссплатформа?
В шоу ведущий сталкивает двух представителей из разных сфер в IT и накидывает спорные тезисы, а затем участники обсуждают согласны ли они с этими утверждениями или нет.
Если вам по душе подобный формат, то тут оставил плейлист со всеми выпусками.
Please open Telegram to view this post
VIEW IN TELEGRAM
Custom Quick Settings tiles
Забавно, что являясь Android разработчиком, мне очень редко приходится решать задачи, связанные с самой платформой и ее фичами, и, думаю, я такой не один. Но недавно появилась потребность добавить свою кастомную плитку для приложения в шторку с быстрыми настройками.
Каждая такая плитка представляет из себя специальный сервис под названием
Проблема только в том, что нашел я этот гайд уже после того как сам разобрался, что к чему, ведь по запросу😒
Главное, что нужно знать, что обновлять плитку можно только в промежутке между🤔 . Как по мне, первый способ является более удобным, но разумеется все зависит от конкретного кейса.
⚠️Однако я столкнулся с одной проблемой, само по себе взаимодействие с
💬 А используете ли вы данную функциональность в своих приложениях? Если да, то какие сценарии вы туда выносите?
#Android
@kotlin_adept
Забавно, что являясь Android разработчиком, мне очень редко приходится решать задачи, связанные с самой платформой и ее фичами, и, думаю, я такой не один. Но недавно появилась потребность добавить свою кастомную плитку для приложения в шторку с быстрыми настройками.
Каждая такая плитка представляет из себя специальный сервис под названием
TileService
. Это особый вид bound сервиса, который имеет свои особенности жизненного цикла и все нюансы подробно описаны в гайде. Проблема только в том, что нашел я этот гайд уже после того как сам разобрался, что к чему, ведь по запросу
TileService
отдавало все что угодно: скудную документацию, статьи подозрительного качества, но только не гайд Главное, что нужно знать, что обновлять плитку можно только в промежутке между
onStartListening()
и onStopListening()
и по умолчанию этот промежуток соответствует поведению, когда плитка видна на экране у пользователя, что выглядит довольно логично и похоже на то, как мы подписываемся на стейт в UI, но Google почему-то рекомендует явно запрашивать listening state ⚠️Однако я столкнулся с одной проблемой, само по себе взаимодействие с
TileService
не переводит ваше приложение в состояние foreground и вы можете столкнуться с различными ограничениями системы. В моем случае помог запуск отдельного foreground сервиса из TileService.#Android
@kotlin_adept
Please open Telegram to view this post
VIEW IN TELEGRAM
Нашли серьезную уязвимость в Jetpack Navigation Compose, которая позволяет открыть любой экран в приложении, даже если там нет явных диплинков ⚠️
Эксплуатируется она максимально просто, достаточно знать имя пакета и название маршрута в графе навигации:
Как защититься
1. Разумеется лучший вариант не использовать данную навигацию, можете посмотреть мой пост со сравнением библиотек навигации для Compose и выбрать подходящую
2. Если в приложении не используются диплинки, можно частично решить проблему перетерев data в определенном intent:
#Security #Compose
@kotlin_adept
Эксплуатируется она максимально просто, достаточно знать имя пакета и название маршрута в графе навигации:
Intent().apply {
setClassName("your.package", "your.package.MainActivity")
data = Uri.parse("android-app://androidx.navigation/YOUR_DESTINATION")
startActivity(this)
}
Как защититься
1. Разумеется лучший вариант не использовать данную навигацию, можете посмотреть мой пост со сравнением библиотек навигации для Compose и выбрать подходящую
2. Если в приложении не используются диплинки, можно частично решить проблему перетерев data в определенном intent:
val intentData = intent.dataString
if (intentData != null && intentData.startsWith("android-app://androidx.navigation")) {
intent.setData(null)
}
#Security #Compose
@kotlin_adept
Please open Telegram to view this post
VIEW IN TELEGRAM
Оператор RxJava, изменивший все
Недавно взялся за небольшой старый проект, в котором все еще большинство приложения было написано на RxJava, и я по всем правилам бойскаута переводил только новые куски на корутины до вчерашнего дня. Пока не пришлось столкнуться с огромной цепочкой, состоящей по меньшей мере из 15 операторов👋
Мне всего лишь нужно было добавить одно условие в оператор💁♂️
Тут новое поколение скажет: "Дед, ты таблетки забыл выпить, какая нафиг RxJava, какой еще Maybe"
В общем потупив минут 5, я решил, что оно того не стоит и переписал всю цепочку на корутины, а затем и весь проект... Иии... Результат того стоил!
👍 Все места, где использовались Single и Maybe стали обычными suspend функциями с последовательным кодом
👍 Эта сложная Rx цепочка вообще превратилась в цепочку котлиновских операторов над коллекцией
👍 Теперь нигде не нужно было явно переключать диспатчер
👍 А главное, что я переписал весь проект с первого раза, без единого бага 💪
👎 Без минусов, конечно, тоже не обошлось, в стандартной библиотеке Flow не всегда хватает операторов, например мне не хватило
Так что, подводя итог, я в очередной раз убедился, что несмотря на недостатки корутин, они значительно выигрывают у RxJava в плане читаемости и простоте кода, и нужно активно переходить на них в своих проектах, если вы еще этого не сделали, иначе скоро вы не сможете найти человека, который знает, что такое Maybe в RxJava.
💬 А в ваших проектах еще остался Rx?
#RxJava #Coroutines
@kotlin_adept
Недавно взялся за небольшой старый проект, в котором все еще большинство приложения было написано на RxJava, и я по всем правилам бойскаута переводил только новые куски на корутины до вчерашнего дня. Пока не пришлось столкнуться с огромной цепочкой, состоящей по меньшей мере из 15 операторов
Мне всего лишь нужно было добавить одно условие в оператор
onErrorResumeNext
, но то, как это делается с типом Single, абсолютно не подходит для типа Maybe, а казалось бы какая разница В общем потупив минут 5, я решил, что оно того не стоит и переписал всю цепочку на корутины, а затем и весь проект... Иии... Результат того стоил!
throttleFirst
и interval
, благо я знаю, где их искать и как написать самому, потому что с Flow кастомные операторы — это супер просто!Так что, подводя итог, я в очередной раз убедился, что несмотря на недостатки корутин, они значительно выигрывают у RxJava в плане читаемости и простоте кода, и нужно активно переходить на них в своих проектах, если вы еще этого не сделали, иначе скоро вы не сможете найти человека, который знает, что такое Maybe в RxJava.
#RxJava #Coroutines
@kotlin_adept
Please open Telegram to view this post
VIEW IN TELEGRAM
По мотивам прошлого поста, у людей возник вопрос, а что сложного в обработке исключений в корутинах?
Давайте посмотрим на маленьком примере, в каких случаях приложение скрашится при возникновении исключения в suspend функции getException?
Давайте посмотрим на маленьком примере, в каких случаях приложение скрашится при возникновении исключения в suspend функции getException?
В каких случаях приложение скрашится при возникновении исключения в suspend функции getException?
Anonymous Quiz
7%
1,2
18%
1,3
45%
1,4
6%
2,3
7%
2,4
17%
3,4
Канал незаметно перевалил за тысячу подписчиков 🎉
Спасибо, что читаете мои редкие записки!
В честь этого давайте разыграем Telegram Premium(поддержим Пашу и я смогу редактировать оформление канала 👏 ), а также чуть позже будет розыгрыш билета на следующий сезон Podlodka Android Crew, так что stay tuned 🎩
Спасибо, что читаете мои редкие записки!
В честь этого давайте разыграем Telegram Premium
Please open Telegram to view this post
VIEW IN TELEGRAM
Топ 4 незаметных бага при смене стека
Помните я похвастался, что переписал проект с RX на корутины без багов? Так вот я соврал😼
Ну или не совсем, так как баги не связаны напрямую с асинхронщиной, поэтому давайте посмотрим на топ незаметных багов при смене стека технологий.
1️⃣ Переходя с Rx на корутины у вас может сломаться Retrofit, при этом только для специфичных кейсов, когда в ответе приходит код 204, Retrofit начинает воспринимать успешный ответ как ошибку из-за отсутствия Body.
2️⃣ Переходя с Retrofit на Ktor проверьте все экстеншены для определения сетевых ошибок, зависимость на OkHttp у вас останется, но работать экстеншены перестанут так как исключение уже будет не HttpException, а ResponseException.
3️⃣ А также проверьте места, где возвращается Response, а не Body, в Retrofit это всегда успешная операция вне зависимости от ответа, в Ktor же это будет ошибкой, если выставлен флаг expectSuccess.
4️⃣ При переходе с GSON на KotlinxSerialization убедитесь, что все опциональные поля моделей помечены nullable и имеют дефолтное значение, так как GSON мог засунуть null даже в non-nullable поле и все могло работать, если к этому полю не обращаться.
💭 А какие незаметные баги просачивались у вас при рефакторинге стека технологий?
#Android #Kotlin
@kotlin_adept
Помните я похвастался, что переписал проект с RX на корутины без багов? Так вот я соврал
Ну или не совсем, так как баги не связаны напрямую с асинхронщиной, поэтому давайте посмотрим на топ незаметных багов при смене стека технологий.
#Android #Kotlin
@kotlin_adept
Please open Telegram to view this post
VIEW IN TELEGRAM
Kotlin Adept Notes
Please open Telegram to view this post
VIEW IN TELEGRAM
Kotlin Adept Notes
Поздравляю победителя, но никому не советую разыгрывать Telegram Premium, если вы вдруг собирались.
По итогу пришлось забанить 195 ботов в канале😀 и сделать на сегодня канал частным, чтобы не налетело ещё больше ботов на розыгрыш.
Очень странно, что ТГ сам не умеет отсеивать ботов, в целом там довольно понятные паттерны как их можно определять🤔
По итогу пришлось забанить 195 ботов в канале
Очень странно, что ТГ сам не умеет отсеивать ботов, в целом там довольно понятные паттерны как их можно определять🤔
Please open Telegram to view this post
VIEW IN TELEGRAM
Продолжим череду розыгрышей на канале, мы с ребятами подготовили для вас очередной сезон Podlodka Android Crew про автоматизацию разработки.
Удалось собрать сильных спикеров и интересную программу, но думаю вы и без меня уже видели кучу рекламы в каналах, поэтому повторяться не буду. Перейдем сразу к делу, и разыграем билет на конференцию.
От вас всего два условия:
1. Быть подписанным на канал @kotlin_adept
2. Написать любой комментарий про автоматизацию разработки. Может есть что-то, что надоело делать вручную или, наоборот, вы что-то уже автоматизировали
Победителя выберем рандомно среди всех, кто выполнил условия, и подведем итоги 14 сентября.
Ну а те, кто не верит в фортуну, ловите промокод на скидку: android_crew_12_o1TLih
Please open Telegram to view this post
VIEW IN TELEGRAM
iOS библиотеки в Kotlin коде
Не все знают, что в KMP мы не только можем скомпилировать Kotlin код в iOS фреймворк, но и, наоборот, использовать сторонние iOS библиотеки в Kotlin коде.
▶️ Например, есть две нативные библиотеки под Android и iOS, и вы хотите объединить их в одну KMP библиотеку и сделать все на Kotlin, тогда у вас есть два пути:
1️⃣ Использовать cocoapods (сторонний менеджер зависимостей в iOS), тогда подключение iOS фреймворка делается в одну строчку кода
2️⃣ Подключать фреймворк вручную, но тут все намного сложнее
Проблема cocoapods в том, что недавно их перевели в режим поддержки и большинство iOS разработчиков мигрируют свои зависимости на официальное решение SPM (Swift Package Manager), поэтому пойдем по второму пути и подключим iOS framework вручную:
1. Необходимо создать файл с расширением .def, где опишем некоторые параметры:
2. В build.gradle вашей библиотеки необходимо добавить конфигурацию для создания klib из фреймворка с помощью cinterop🤯 :
Сделать это необходимо для всех iOS таргетов❗️ Также как и в следующем шаге.
3. В build.gradle файле, где будете собирать итоговый фреймворк для iOS нужно прилинковать тот же фреймворк:
💡 Как видите, делается это довольно сложно, поэтому не рекомендую использовать сторонние iOS библиотеки в Kotlin коде, лучше в общем коде сделать интерфейс, а реализацию оставить нативной на каждой платформе.
❌ А еще если в iOS проекте уже использовалась эта библиотека, но другой версии, то все конечно же развалится 😂
#KMP #Kotlin #iOS
@kotlin_adept
Не все знают, что в KMP мы не только можем скомпилировать Kotlin код в iOS фреймворк, но и, наоборот, использовать сторонние iOS библиотеки в Kotlin коде.
Проблема cocoapods в том, что недавно их перевели в режим поддержки и большинство iOS разработчиков мигрируют свои зависимости на официальное решение SPM (Swift Package Manager), поэтому пойдем по второму пути и подключим iOS framework вручную:
1. Необходимо создать файл с расширением .def, где опишем некоторые параметры:
language = Objective-C
modules = YourFrameworkName
package = YourFrameworkName
2. В build.gradle вашей библиотеки необходимо добавить конфигурацию для создания klib из фреймворка с помощью cinterop
KotlinNativeTarget.compilations.getByName("main") {
val YourFramework by cinterops.creating {
defFile = project.file("src/nativeInterop/cinterop/YourFramework.def")
compilerOpts("-framework", "YourFramework", "-F${projectDir}/../YourFramework.xcframework/$frameworkArch/")
compilerOpts("-fmodules")
}
}
Сделать это необходимо для всех iOS таргетов
3. В build.gradle файле, где будете собирать итоговый фреймворк для iOS нужно прилинковать тот же фреймворк:
KotlinNativeTarget.binaries.all {
linkerOpts("-framework", "YourFramework", "-F${projectDir}/../YourFramework.xcframework/$frameworkArch/")
}
#KMP #Kotlin #iOS
@kotlin_adept
Please open Telegram to view this post
VIEW IN TELEGRAM
Как вкатиться в KMP без MacOs
Сегодня, слушая доклад Никиты Куликова на Podlodka Android Crew, получил для себя интересный инсайт.
Довольно часто видел вопросы в стиле, как мне вкатиться в Kotlin Multiplatform, если под рукой нет мака? Ведь на другой ОС запустить приложение под iOS не выйдет.
Решение довольно интересное, GitHub предоставляет вам бесплатное и безлимитное использование разных раннеров для Open source проектов, в том числе и на MacOs, соответственно вы можете собирать iOS проекты на CI и затем тестировать их на iPhone, если он у вас конечно есть🙃
Если хотите больше таких инсайтов и интересных обсуждений с разными экспертами, то вы ещё успеваете залететь на конференцию со скидкой по промокоду android_crew_12_o1TLih😉
#KMP
@kotlin_adept
Сегодня, слушая доклад Никиты Куликова на Podlodka Android Crew, получил для себя интересный инсайт.
Довольно часто видел вопросы в стиле, как мне вкатиться в Kotlin Multiplatform, если под рукой нет мака? Ведь на другой ОС запустить приложение под iOS не выйдет.
Решение довольно интересное, GitHub предоставляет вам бесплатное и безлимитное использование разных раннеров для Open source проектов, в том числе и на MacOs, соответственно вы можете собирать iOS проекты на CI и затем тестировать их на iPhone, если он у вас конечно есть
Если хотите больше таких инсайтов и интересных обсуждений с разными экспертами, то вы ещё успеваете залететь на конференцию со скидкой по промокоду android_crew_12_o1TLih
#KMP
@kotlin_adept
Please open Telegram to view this post
VIEW IN TELEGRAM