Как перехватывать Exception в Coroutine. Часть 4
Разберемся с билдером launch
Рабочие способы перехватить ошибки:
• внутри launch
launch {
try {
throw Exception()
} catch(e: Exception) {}
}
• с помощью перехватчика необработанных ошибок, установленного в Scope или launch верхнего уровня
scope.launch(handler) {
throw Exception()
}
СoroutineScope(handler) {
launch {
throw Exception()
}
}
в этом способе важно понимать — если родитель Scope(Job), дети этого Scope будут остановлены
если родитель Scope(SupervisorJob), дети этого Scope продолжат работу
если родитель корутина, то дети этой корутины будут остановлены в любом случае
Что будет, если не обработать:
ошибка будет распространена родительским корутинам и Scope, пока не попадет в CoroutineExceptionHandler или Thread.UncaughtExceptionHandler
Что не получится сделать:
• обернуть launch в try-catch
такое не сработает:
try {
launch { throw Exception() }
} catch(e: Exception) {}
потому что launch распространяет ошибку родителю, а не выбрасывает
....
если вы встретили примеры, в которых не очевидно поведение корутин при возникновении ошибки — скидывайте их в комментарии, разберем)
Разберемся с билдером launch
Рабочие способы перехватить ошибки:
• внутри launch
launch {
try {
throw Exception()
} catch(e: Exception) {}
}
• с помощью перехватчика необработанных ошибок, установленного в Scope или launch верхнего уровня
scope.launch(handler) {
throw Exception()
}
СoroutineScope(handler) {
launch {
throw Exception()
}
}
в этом способе важно понимать — если родитель Scope(Job), дети этого Scope будут остановлены
если родитель Scope(SupervisorJob), дети этого Scope продолжат работу
если родитель корутина, то дети этой корутины будут остановлены в любом случае
Что будет, если не обработать:
ошибка будет распространена родительским корутинам и Scope, пока не попадет в CoroutineExceptionHandler или Thread.UncaughtExceptionHandler
Что не получится сделать:
• обернуть launch в try-catch
такое не сработает:
try {
launch { throw Exception() }
} catch(e: Exception) {}
потому что launch распространяет ошибку родителю, а не выбрасывает
....
если вы встретили примеры, в которых не очевидно поведение корутин при возникновении ошибки — скидывайте их в комментарии, разберем)
❤7👍4🎉1
Как перехватывать Exception в Coroutine. Часть 5 (финал)
Разберемся с билдером async
Рабочие способы обработки ошибки:
• внутри async
async {
try {
throw Exception()
} catch(e: Exception) {}
}
• с помощью try-catch, если async верхнего уровня
Ошибка выбрасывается во время вызова await()
runBlocking {
val job = scope.async {
throwError()
}
tryCatch {
job.await()
}
}
Что будет, если не обработать:
ошибка будет распространяться родительским корутинам и Scope, пока не попадет в CoroutineExceptionHandler или Thread.UncaughtExceptionHandler
....
в комментариях оставлю ссылку на Kotlin Playground с тестами, на которых можно проверить свое чутье — где и кем будет перехвачена ошибка
Разберемся с билдером async
Рабочие способы обработки ошибки:
• внутри async
async {
try {
throw Exception()
} catch(e: Exception) {}
}
• с помощью try-catch, если async верхнего уровня
Ошибка выбрасывается во время вызова await()
runBlocking {
val job = scope.async {
throwError()
}
tryCatch {
job.await()
}
}
Что будет, если не обработать:
ошибка будет распространяться родительским корутинам и Scope, пока не попадет в CoroutineExceptionHandler или Thread.UncaughtExceptionHandler
....
в комментариях оставлю ссылку на Kotlin Playground с тестами, на которых можно проверить свое чутье — где и кем будет перехвачена ошибка
🔥5❤1
GET vs POST vs PUT в Retrofit 2 — уровень "Мобильный разработчик"
Вероятно, если вы человек-бекендер, то нужно углубляться сильнее. А для нас достаточно следующих фактов:
Указывая аннотации @GET, @POST или @PUT, вы определяете, в каком виде будут переданы параметры запроса на сервер
• GET
Параметры запроса вставляются в URL запроса с помощью аннотации Query(parameterName) в формате key=value
Параметры добавляются после знака "?"
Длина URL ограничена 2048 символами
Например, на выходе можете получить такой запрос: https://www.google.com/search?q=tg+dolgo.polo+dev
key = q
value = tg+dolgo.polo+dev
• POST
Параметры запроса вставляются не в URL, а в поле requestBody с помощью аннотации @Body в формате key=value
POST-запросом можно отправить файл, так как он поддерживает отправку бинарных данных
Например, на выходе можете получить такой запрос: https://www.google.com/search
Параметры при этом передаются отдельно в поле requestBody
• PUT
То же самое, что и POST. Нужен для логического разделения ролей запросов в API (просто другое название)
POST — обновляет данные по какому-то правилу
например, увеличивает количество яблок в коробке: apples++
PUT — устанавливает данные
например, устанавливает количество яблок в коробке: apples = 1
Поэтому говорят, PUT — идемпотентный — можно отправлять один и тот же запрос много раз, данные изменятся только один раз
Еще есть мнение, что POST для списков, а PUT для обращения по конкретному id
....
а менее известные DELETE, PATCH, OPTIONS, CONNECT и TRACE зачем и когда используются?
Вероятно, если вы человек-бекендер, то нужно углубляться сильнее. А для нас достаточно следующих фактов:
Указывая аннотации @GET, @POST или @PUT, вы определяете, в каком виде будут переданы параметры запроса на сервер
• GET
Параметры запроса вставляются в URL запроса с помощью аннотации Query(parameterName) в формате key=value
Параметры добавляются после знака "?"
Длина URL ограничена 2048 символами
Например, на выходе можете получить такой запрос: https://www.google.com/search?q=tg+dolgo.polo+dev
key = q
value = tg+dolgo.polo+dev
• POST
Параметры запроса вставляются не в URL, а в поле requestBody с помощью аннотации @Body в формате key=value
POST-запросом можно отправить файл, так как он поддерживает отправку бинарных данных
Например, на выходе можете получить такой запрос: https://www.google.com/search
Параметры при этом передаются отдельно в поле requestBody
• PUT
То же самое, что и POST. Нужен для логического разделения ролей запросов в API (просто другое название)
POST — обновляет данные по какому-то правилу
например, увеличивает количество яблок в коробке: apples++
PUT — устанавливает данные
например, устанавливает количество яблок в коробке: apples = 1
Поэтому говорят, PUT — идемпотентный — можно отправлять один и тот же запрос много раз, данные изменятся только один раз
Еще есть мнение, что POST для списков, а PUT для обращения по конкретному id
....
а менее известные DELETE, PATCH, OPTIONS, CONNECT и TRACE зачем и когда используются?
👍20❤4🐳2👎1🙏1
Чек-лист профессионального программиста
Это свод правил, выработанный за годы фриланса
Когда соблюдаю их, приложение получается удобным для юзера
Да, этот список не сделает из вас сеньора, как и не делает им меня. Но это как с мужем-на-час, который не только починил кран, но и убрал за собой рабочее место — сразу видно, крутой
• сортировать списки
если список выводится пользователю, нужно не забыть понятную сортировку - по дате создания или алфавиту, если нет других заданных параметров
• проверять нули, пустые строки и нуллы, количество элементов в списке
продумайте, что увидит юзер. например, если список пуст, не забыть вывести "оëой, данных пока нет"
и не выводить "ваша скидка 0% !!!"
• поднимать ошибку по стеку в обертке
если вы запрашиваете Long из базы данных, то возвращать -1 в случае ошибки не лучшая идея. Лучше вернуть Success(value) или Error(errorCode)
• использовать варианты toDoubleOrNull() вместо toDouble()
никогда не надейтесь, что преобразование строки в число пройдёт успешно
а вдруг кто-то подсунет строку с запятой вместо точки?
приложение крашнется с ошибкой NotNumber, а юзер умрёт от инфаркта
лучше добавить лишнюю проверку на null (или обернуть в try-catch)
• сообщать о загрузке
если контент загружается больше 500мс, отобразите индикатор загрузки или скелет будущего контента
• помнить, что юзер может закрыть приложение в любой момент, но операция должна завершиться
юзер не обязан ждать, пока данные сохранятся - может свернуть приложение, заблокировать телефон, уронить его в лужу - операция должна выполниться в фоне
Это свод правил, выработанный за годы фриланса
Когда соблюдаю их, приложение получается удобным для юзера
Да, этот список не сделает из вас сеньора, как и не делает им меня. Но это как с мужем-на-час, который не только починил кран, но и убрал за собой рабочее место — сразу видно, крутой
• сортировать списки
если список выводится пользователю, нужно не забыть понятную сортировку - по дате создания или алфавиту, если нет других заданных параметров
• проверять нули, пустые строки и нуллы, количество элементов в списке
продумайте, что увидит юзер. например, если список пуст, не забыть вывести "оëой, данных пока нет"
и не выводить "ваша скидка 0% !!!"
• поднимать ошибку по стеку в обертке
если вы запрашиваете Long из базы данных, то возвращать -1 в случае ошибки не лучшая идея. Лучше вернуть Success(value) или Error(errorCode)
• использовать варианты toDoubleOrNull() вместо toDouble()
никогда не надейтесь, что преобразование строки в число пройдёт успешно
а вдруг кто-то подсунет строку с запятой вместо точки?
приложение крашнется с ошибкой NotNumber, а юзер умрёт от инфаркта
лучше добавить лишнюю проверку на null (или обернуть в try-catch)
• сообщать о загрузке
если контент загружается больше 500мс, отобразите индикатор загрузки или скелет будущего контента
• помнить, что юзер может закрыть приложение в любой момент, но операция должна завершиться
юзер не обязан ждать, пока данные сохранятся - может свернуть приложение, заблокировать телефон, уронить его в лужу - операция должна выполниться в фоне
👍27❤7🏆4🤔2
Стартовал мой второй серьезный проект — место, где можно выкладывать свои идеи и очень обоснованно критиковать чужие
tg: mvpшная
• зачем это нужно?
появляется идея. она кажется тебе неплохой (принесет успех, деньги и внутренний покой), но ведь оцениваешь ее только через призму своего опыта
а можешь сразу обсудить ее с другими крутыми людьми, которые увидят слабые места
например, можете залететь с идеями своих приложений или сервисов. узнаете, на чем можете споткнуться. или найдете инвесторов исобутыльников единомышленников
"а вдруг украдут!" - если у вашу идею можно легко украсть, то либо это уже делают тысячи людей, либо вы что-то упускаете в расчетах. и как раз обсуждение с незнакомыми людьми поможет узнать, что забываешь учесть
tg: mvpшная
• зачем это нужно?
появляется идея. она кажется тебе неплохой (принесет успех, деньги и внутренний покой), но ведь оцениваешь ее только через призму своего опыта
а можешь сразу обсудить ее с другими крутыми людьми, которые увидят слабые места
например, можете залететь с идеями своих приложений или сервисов. узнаете, на чем можете споткнуться. или найдете инвесторов и
Telegram
mvpшная
Место, где можно опубликовать идею своего бизнеса (любого размера) и получить качественный фидбек
Читателям предлагается безжалостно указывать на все слабые места и будущие проблемы идеи в комментариях
@messages_receiver — прием заявок
Читателям предлагается безжалостно указывать на все слабые места и будущие проблемы идеи в комментариях
@messages_receiver — прием заявок
👍3❤1😁1🤔1
Kotlin Flow: StateFlow vs SharedFlow
Часть №1
Сначала общие факты для понимания задумки авторов библиотеки Kotlin Flow:
• Flow (потоки) — никак не связаны с Android
это библиотека котлина, которая завязано только на корутины (которые тоже являются библиотекой, не связанной с Android SDK)
• основные методы:
collect() - подписаться на данные из потока
emit() - положить значение в поток
• холодный поток — запускает создание элементов каждый раз при вызове collect()
при объявление потока описывается функция-билдер новых элементов, внутри которого вызывается emit()
то есть для каждого подписчика свой поток данных
• горячий поток — не зависит от вызова collect()
то есть для каждого подписчика один и тот же поток данных
• если корутина, запустившая холодный поток, остановлена — поток тоже остановится
но только если внутри есть проверка флага isActive
• collect() и emit() — suspend-функции, выполнение корутины не продолжится, пока они не завершатся
collect() приостанавливает корутину до завершения потока данных
emit() приостанавливает корутину до момента, пока поток данных не будет готов принять элемент (освободится буфер)
• CoroutineContext определяется родительской корутиной, вызвавшей collect(). но его можно сменить с помощью оператора flowOn(CouroutineContext)
например, можно получать элементы из Flow в главном потоке, а выполнять их создание в бэкграунде
• зачем нужен буфер?
если речь идет о холодном потоке, то буфер помогает выполнить emit(), не дожидаясь завершения collect()
если речь идет о горячем потоке, то буфер определяет, сколько последних элементов получит новый подписчик, вызвавший collect()
Часть №1
Сначала общие факты для понимания задумки авторов библиотеки Kotlin Flow:
• Flow (потоки) — никак не связаны с Android
это библиотека котлина, которая завязано только на корутины (которые тоже являются библиотекой, не связанной с Android SDK)
• основные методы:
collect() - подписаться на данные из потока
emit() - положить значение в поток
• холодный поток — запускает создание элементов каждый раз при вызове collect()
при объявление потока описывается функция-билдер новых элементов, внутри которого вызывается emit()
то есть для каждого подписчика свой поток данных
• горячий поток — не зависит от вызова collect()
то есть для каждого подписчика один и тот же поток данных
• если корутина, запустившая холодный поток, остановлена — поток тоже остановится
но только если внутри есть проверка флага isActive
• collect() и emit() — suspend-функции, выполнение корутины не продолжится, пока они не завершатся
collect() приостанавливает корутину до завершения потока данных
emit() приостанавливает корутину до момента, пока поток данных не будет готов принять элемент (освободится буфер)
• CoroutineContext определяется родительской корутиной, вызвавшей collect(). но его можно сменить с помощью оператора flowOn(CouroutineContext)
например, можно получать элементы из Flow в главном потоке, а выполнять их создание в бэкграунде
• зачем нужен буфер?
если речь идет о холодном потоке, то буфер помогает выполнить emit(), не дожидаясь завершения collect()
если речь идет о горячем потоке, то буфер определяет, сколько последних элементов получит новый подписчик, вызвавший collect()
❤19👍13❤🔥1🥰1
Kotlin Flow: StateFlow vs SharedFlow
Часть №2
Общее для StateFlow и SharedFlow:
• это горячие потоки
то есть вызов collect() лишь позволяет собрать данные, которые уже в них есть или поступят в них, но не запускает выполнение какого-либо билдера (как это происходит с холодными потоками)
• они не отслеживает жизненный цикл активити\фрагмента\вьюшки
но их можно запустить в корутинах, которые автоматически остановятся во время вызова onStop(). остановка \корутины вызывает остановку выполнения collect()
Отличия StateFlow и SharedFlow:
• при создании StateFlow нужно указать начальное значение, а при создании SharedFlow — нет
• у SharedFlow можно настроить вместимость буфера, у StateFlow хранится только 1 последний элемент
У SharedFlow два буфера — replayBuffer и extraBuffer:
• replay — сколько новый подписчик, вызвавший collect(), получит последних выпущенных элементов
• extraBuffer — сколько дополнительно элементов сохранится для "медленного" подписчика, который не успевает выполнять collect()
этот буфер работает в следующей ситуации:
допустим, emit() вызывается каждые 100мс, а collect() выполняется за 150мс
• onBufferOverflow — определяет, что делать, когда вызывается emit() при полностью заполненном extraBuffer
сценарии:
SUSPEND (остановить корутину на строчке emit, пока не освободится буфер)
DROPLATEST (удалить последний элемент в буфере)
DROPOLDEST (удалить самый старый элемент в буфере)
эти сценарии включаются в работу только если есть хотя бы один подписчик, который не успевает выполнять collect(). они не влияют на работу replayBuffer
Часть №2
Общее для StateFlow и SharedFlow:
• это горячие потоки
то есть вызов collect() лишь позволяет собрать данные, которые уже в них есть или поступят в них, но не запускает выполнение какого-либо билдера (как это происходит с холодными потоками)
• они не отслеживает жизненный цикл активити\фрагмента\вьюшки
но их можно запустить в корутинах, которые автоматически остановятся во время вызова onStop(). остановка \корутины вызывает остановку выполнения collect()
Отличия StateFlow и SharedFlow:
• при создании StateFlow нужно указать начальное значение, а при создании SharedFlow — нет
• у SharedFlow можно настроить вместимость буфера, у StateFlow хранится только 1 последний элемент
У SharedFlow два буфера — replayBuffer и extraBuffer:
• replay — сколько новый подписчик, вызвавший collect(), получит последних выпущенных элементов
• extraBuffer — сколько дополнительно элементов сохранится для "медленного" подписчика, который не успевает выполнять collect()
этот буфер работает в следующей ситуации:
допустим, emit() вызывается каждые 100мс, а collect() выполняется за 150мс
• onBufferOverflow — определяет, что делать, когда вызывается emit() при полностью заполненном extraBuffer
сценарии:
SUSPEND (остановить корутину на строчке emit, пока не освободится буфер)
DROPLATEST (удалить последний элемент в буфере)
DROPOLDEST (удалить самый старый элемент в буфере)
эти сценарии включаются в работу только если есть хотя бы один подписчик, который не успевает выполнять collect(). они не влияют на работу replayBuffer
👍18🔥5❤1
Рассказ о стартапе
У меня появилась боль — когда я хотел потратить свободное время, у меня из головы вылетали все идеи
Начал вести список развлечений — оказалось, что без фильтров это очень неудобно
Решил делать приложения (не зря же умею). А чтобы оно не умерло до релиза, решил обернуть это все в стартап и завести отдельный канал, где рассказываю о трудностях в разработке
Если вам интересно, с какими факапами можно столкнуться при запуске своего проекта (от поиска иконок до оформления документов) и пройти путь от зарождения идеи до релиза (или удаления папки с проектом) со мной, то раз в пару дней посты об этом будут появляться там — t.me/find_fun_app
....
в этом канале скоро будут посты о болях с точки зрения кода — почему мне пришлось писать свой LazyList, как сделать кастомную анимацию на Compose и как вкатиться в многомодульную архитектуру, если ты скромный проект на пару тысяч строк (и нужно ли это)
У меня появилась боль — когда я хотел потратить свободное время, у меня из головы вылетали все идеи
Начал вести список развлечений — оказалось, что без фильтров это очень неудобно
Решил делать приложения (не зря же умею). А чтобы оно не умерло до релиза, решил обернуть это все в стартап и завести отдельный канал, где рассказываю о трудностях в разработке
Если вам интересно, с какими факапами можно столкнуться при запуске своего проекта (от поиска иконок до оформления документов) и пройти путь от зарождения идеи до релиза (или удаления папки с проектом) со мной, то раз в пару дней посты об этом будут появляться там — t.me/find_fun_app
....
в этом канале скоро будут посты о болях с точки зрения кода — почему мне пришлось писать свой LazyList, как сделать кастомную анимацию на Compose и как вкатиться в многомодульную архитектуру, если ты скромный проект на пару тысяч строк (и нужно ли это)
🔥11👌5❤2👍2
Kotlin — вложенные и внутренние классы
Кроме наследования, есть два типа отношений между классами:
• вложенные (nested)
class A {
class B
}
создать экземпляр класса B: A.B()
зачем: выделить часть функционала класса A, используемый только с классом A, в отдельный класс B
• внутренние (inner)
class A {
inner class B
}
создать экземпляр класса B: A().B()
зачем: аналогично вложенному, но при этом функционал, выделенный в класс B, зависит от полей класса A
Отличия:
вложенный класс не связан с экземпляром внешнего — они создаются отдельно
внутренние классы содержат ссылку на внешний класс, то есть во внутренний класс неявно передается ссылка на внешний класс
у внутреннего класса есть доступ к public, private и protected полям внешнего класса
Возможная опасность:
передавая куда-то внутренний класс, нужно помнить, что он содержит ссылку на внешний. это может привести к утечки памяти
....
есть примеры из Android SDK, библиотек или личного опыта, когда использование вложенных или внутренних классов было действительно оправдано?
Кроме наследования, есть два типа отношений между классами:
• вложенные (nested)
class A {
class B
}
создать экземпляр класса B: A.B()
зачем: выделить часть функционала класса A, используемый только с классом A, в отдельный класс B
• внутренние (inner)
class A {
inner class B
}
создать экземпляр класса B: A().B()
зачем: аналогично вложенному, но при этом функционал, выделенный в класс B, зависит от полей класса A
Отличия:
вложенный класс не связан с экземпляром внешнего — они создаются отдельно
внутренние классы содержат ссылку на внешний класс, то есть во внутренний класс неявно передается ссылка на внешний класс
у внутреннего класса есть доступ к public, private и protected полям внешнего класса
Возможная опасность:
передавая куда-то внутренний класс, нужно помнить, что он содержит ссылку на внешний. это может привести к утечки памяти
....
есть примеры из Android SDK, библиотек или личного опыта, когда использование вложенных или внутренних классов было действительно оправдано?
👍21🔥3❤1
Почему нельзя писать liveData.observe(this...) во Fragment?
Lifecycle — объект, который отслеживает жизненный цикл (Fragment, Activity....) и передает их в качестве состояния тем, кто на этот жизненный цикл подписался (ViewModel, LiveData...)
LifecycleOwner — интерфейс, который содержит единственный метод — getLifecycle() : Lifecycle
LiveData.observe(...) — может принимать ссылку и на Lifecycle, и на LifecycleOwner
С помощью объекта Lifecycle мы сообщаем LiveData, когда данные должны приходить подписчику, а когда — уже нет
Activity и Fragment реализует интерфейс LifecycleOwner
Получается, что в Activity и Fragment мы спокойно можем просто передавать this в LiveData.observe()?
В Activity можем, а во Fragment — можем, но так делать не стоит
Дело в том, что у Fragment жизненный цикл View и самого фрагмента не жестко связаны:
если фрагмент был убран с экрана (onDetach), но добавлен в backstack, то View может быть уничтожена (onDestroyView), а сам фрагмент — нет
получается, если при подписки на LiveData мы передадим ссылку на фрагмент, а не на viewLifecycleOwner, то возможна ситуация, когда данные придут, а View уже уничтожено — приложение упадет
Lifecycle — объект, который отслеживает жизненный цикл (Fragment, Activity....) и передает их в качестве состояния тем, кто на этот жизненный цикл подписался (ViewModel, LiveData...)
LifecycleOwner — интерфейс, который содержит единственный метод — getLifecycle() : Lifecycle
LiveData.observe(...) — может принимать ссылку и на Lifecycle, и на LifecycleOwner
С помощью объекта Lifecycle мы сообщаем LiveData, когда данные должны приходить подписчику, а когда — уже нет
Activity и Fragment реализует интерфейс LifecycleOwner
Получается, что в Activity и Fragment мы спокойно можем просто передавать this в LiveData.observe()?
В Activity можем, а во Fragment — можем, но так делать не стоит
Дело в том, что у Fragment жизненный цикл View и самого фрагмента не жестко связаны:
если фрагмент был убран с экрана (onDetach), но добавлен в backstack, то View может быть уничтожена (onDestroyView), а сам фрагмент — нет
получается, если при подписки на LiveData мы передадим ссылку на фрагмент, а не на viewLifecycleOwner, то возможна ситуация, когда данные придут, а View уже уничтожено — приложение упадет
👍33❤3
Многопоточность за 100 слов
Проблема:
есть объект
запускаем два потока
у каждого потока свой кэш, то есть каждый поток создает копию этого объекта, а не работает с ним напрямую
дальше по шагам:
1) первый поток скопировал объект к себе в кэш
2) первый поток изменил поле объекта
3) второй поток скопировал объект к себе в кэш
4) второй поток изменяет это же поле (и не знает об изменениях, сделанных первым потоком!)
5) второй поток записывает изменения в общую память
6) первый поток записывает изменения в общую память
бац — мы потеряли изменения, сделанные во втором потоке. первый поток их перезаписал + второй поток даже не знал, что объект был изменен в первом потоке!
Как этого избежать:
• synchronized — запрещает нескольким потокам работать с объектом (классом) одновременно
перед тем как выполнить участок кода, поток захватывает объект (класс), а другие потоки ждут освобождения объекта (класса)
- synchronized(Object) — захватывает объект
- synchronized fun — можно читать как synchronized(this), захватывает объект
- static synchronized fun — захватывает класс (а не объект)
• volatile — синхронизирует чтение и запись
сработает, если один поток только читает, а другой только пишет
не сработает, если оба потока изменяют объект — потому что операции между чтением и записью не синхронизированы
• atomic
атомарная операция = неделимая операция изменения объекта
с объектами, имеющими атомарные операции изменения (например, увеличение на единицу для AtomicInteger), можно работать в разных потоках без ручной синхронизации
в Java есть атомарные примитивы, коллекции и класс AtomicReference для работы с произвольными классами
....
а нужно ли думать о синхронизации переменной в корутинах?
сработают ли там эти подходы?
Проблема:
есть объект
запускаем два потока
у каждого потока свой кэш, то есть каждый поток создает копию этого объекта, а не работает с ним напрямую
дальше по шагам:
1) первый поток скопировал объект к себе в кэш
2) первый поток изменил поле объекта
3) второй поток скопировал объект к себе в кэш
4) второй поток изменяет это же поле (и не знает об изменениях, сделанных первым потоком!)
5) второй поток записывает изменения в общую память
6) первый поток записывает изменения в общую память
бац — мы потеряли изменения, сделанные во втором потоке. первый поток их перезаписал + второй поток даже не знал, что объект был изменен в первом потоке!
Как этого избежать:
• synchronized — запрещает нескольким потокам работать с объектом (классом) одновременно
перед тем как выполнить участок кода, поток захватывает объект (класс), а другие потоки ждут освобождения объекта (класса)
- synchronized(Object) — захватывает объект
- synchronized fun — можно читать как synchronized(this), захватывает объект
- static synchronized fun — захватывает класс (а не объект)
• volatile — синхронизирует чтение и запись
сработает, если один поток только читает, а другой только пишет
не сработает, если оба потока изменяют объект — потому что операции между чтением и записью не синхронизированы
• atomic
атомарная операция = неделимая операция изменения объекта
с объектами, имеющими атомарные операции изменения (например, увеличение на единицу для AtomicInteger), можно работать в разных потоках без ручной синхронизации
в Java есть атомарные примитивы, коллекции и класс AtomicReference для работы с произвольными классами
....
а нужно ли думать о синхронизации переменной в корутинах?
сработают ли там эти подходы?
🔥26👍17❤4🐳1
с нг! 🍻🍻🍻
желаю каждому вкусно покушать, порадоваться жизни и всего хо-ро-ше-го!
....
по поводу канала — постов нет не потому что их нет, а потому что копим ману на следующий год
как говорил один разоблачитель клоунов: "Ведь если я способен вернуться — способен каждый!"
желаю каждому вкусно покушать, порадоваться жизни и всего хо-ро-ше-го!
....
по поводу канала — постов нет не потому что их нет, а потому что копим ману на следующий год
как говорил один разоблачитель клоунов: "Ведь если я способен вернуться — способен каждый!"
🎉34👍3🍾3🎄3
Density vs Resolution vs dp vs sp vs dpi
Разбираемся, чем displayMetrics.density отличается от displayMetrics.densityDpi
и фиксируем, когда 1 dp == 1 px
читать
Разбираемся, чем displayMetrics.density отличается от displayMetrics.densityDpi
и фиксируем, когда 1 dp == 1 px
читать
Telegraph
Density vs Resolution vs dp vs sp vs dpi
Screen Resolution == Разрешение экрана количество пикселей например, 320х680 Screen Density == dpi (dots per inch) == ppi (pixels per inch) == Количество пикселей/точек на дюйм == Плотность == resources.displayMetrics.densityDpi обычно мы создаем ресурсы…
👍18❤2🐳2
наша постоянная рубрика — "Вечер рекомендаций"
всё что ниже — не реклама, а просто делюсь годнотой
....
• сообщество Coffee&Code — мобильные разработчики собираются оффлайн (!) каждую неделю
обсуждают айтишные подкасты про смузи, меряются количеством килока в секунду, а потом едут курить кальяны
короче, супер круто, всем советую
вот московский чатик. через него можно выйти на остальные города
иногда сам туда заезжаю, да и в целом там куча крутых мужиков и девчонок, а также разных селебрити)
• канал об Android-разработке — Android Easy Notes
тут сказать особо нечего, просто клево, интересно и не душно. и темы прикольные
....
если тоже знаете что-то крутое(или сами делаете крутое) , айда делиться ссылками в комментариях)
всё что ниже — не реклама, а просто делюсь годнотой
....
• сообщество Coffee&Code — мобильные разработчики собираются оффлайн (!) каждую неделю
обсуждают айтишные подкасты про смузи, меряются количеством килока в секунду, а потом едут курить кальяны
короче, супер круто, всем советую
вот московский чатик. через него можно выйти на остальные города
иногда сам туда заезжаю, да и в целом там куча крутых мужиков и девчонок, а также разных селебрити)
• канал об Android-разработке — Android Easy Notes
тут сказать особо нечего, просто клево, интересно и не душно. и темы прикольные
....
если тоже знаете что-то крутое
❤7🤓6🥰3🤡3👍2🔥2
Удобнее читать посты в
Anonymous Poll
49%
голой телеге (как было в том году)
60%
как отдельные статьи в Telegraph (последние два поста)
Animation — сколько нужно классов, чтобы красиво переместить вьюшку?
Глоссарий:
• интерполятор — функция, описывающая изменение значения во времени
например, значение может изменяться линейно или по экспоненте
• pivotX, pivotY — координата точки внутри View, относительно которой будет применена анимация (например, вращение)
Как можно сделать анимацию:
• ValueAnimator — задаем начальное и конечное значение числа, назначаем интерполятор, вешаем колбек, получаем значение каждые n миллисекунд, делаем с ним что хотим
• ObjectAnimator — то же самое, что и ValueAnimator, только сразу принимает название параметра View, который нужно менять
например, ObjectAnimator.ofFloat(view, "alpha", 1f, 0f)
• ViewPropertyAnimator — оптимизированный ObjectAnimator
имеет более удобный синтаксис, синхронизирует внутри себя анимации над одним и тем же параметром
• LayoutTransition — определяет анимацию появления/удаления элемента
например, есть дефолтная реализация для LinearLayout, включается параметром android:animateLayoutChanges="true"
• Scene — анимация между состояниями экрана
создается две xml — со стартовым и конечным положением вьюшек
• MotionLayout — наследник ConstraintLayout с API для анимации сложных перемещений вьюшек, с которыми взаимодействует пользователь
• WindowAnimation — анимашки перехода между Activity
• AnimationDrawable — описание анимации в drawable
Дополнительное:
• AnimatorSet — упрощает комбинацию анимашек
например, можно запустить анимации последовательно или параллельно с некоторой задержкой
• Fling Animation — придает "физичность" спискам
когда мы быстро свайпаем список, а потом отрываем палец, список сначала быстро разгоняется, а потом медленно останавливается, как будто есть сопротивление воздуха
• Spring animation — придает "физичность" смахиваемым элементам
когда смахиваешь письмо в некоторых почтовых клиентах, оно немного пружинит, как будто отталкивается от соударения с краем экрана
• LottieAnimation — иногда дизайнерам легче скинуть готовый Lottie-файл с анимацией. нужна сторонняя библиотека
....
как дела с анимацией в Compose, все потребности уже закрыты?
Глоссарий:
• интерполятор — функция, описывающая изменение значения во времени
например, значение может изменяться линейно или по экспоненте
• pivotX, pivotY — координата точки внутри View, относительно которой будет применена анимация (например, вращение)
Как можно сделать анимацию:
• ValueAnimator — задаем начальное и конечное значение числа, назначаем интерполятор, вешаем колбек, получаем значение каждые n миллисекунд, делаем с ним что хотим
• ObjectAnimator — то же самое, что и ValueAnimator, только сразу принимает название параметра View, который нужно менять
например, ObjectAnimator.ofFloat(view, "alpha", 1f, 0f)
• ViewPropertyAnimator — оптимизированный ObjectAnimator
имеет более удобный синтаксис, синхронизирует внутри себя анимации над одним и тем же параметром
• LayoutTransition — определяет анимацию появления/удаления элемента
например, есть дефолтная реализация для LinearLayout, включается параметром android:animateLayoutChanges="true"
• Scene — анимация между состояниями экрана
создается две xml — со стартовым и конечным положением вьюшек
• MotionLayout — наследник ConstraintLayout с API для анимации сложных перемещений вьюшек, с которыми взаимодействует пользователь
• WindowAnimation — анимашки перехода между Activity
• AnimationDrawable — описание анимации в drawable
Дополнительное:
• AnimatorSet — упрощает комбинацию анимашек
например, можно запустить анимации последовательно или параллельно с некоторой задержкой
• Fling Animation — придает "физичность" спискам
когда мы быстро свайпаем список, а потом отрываем палец, список сначала быстро разгоняется, а потом медленно останавливается, как будто есть сопротивление воздуха
• Spring animation — придает "физичность" смахиваемым элементам
когда смахиваешь письмо в некоторых почтовых клиентах, оно немного пружинит, как будто отталкивается от соударения с краем экрана
• LottieAnimation — иногда дизайнерам легче скинуть готовый Lottie-файл с анимацией. нужна сторонняя библиотека
....
как дела с анимацией в Compose, все потребности уже закрыты?
👍14❤3🕊3❤🔥1
Много внешних библиотек
После появления более трех модулей в приложении становится сложновато отслеживать, какие либы и какой версии были подключены
Существует несколько способов их упорядочить
читать
После появления более трех модулей в приложении становится сложновато отслеживать, какие либы и какой версии были подключены
Существует несколько способов их упорядочить
читать
Telegraph
Много внешних библиотек
После появления более трех модулей в приложении становится сложновато отслеживать, какие либы и какой версии были подключены Существует несколько способов их упорядочить: • общий модуль с зависимостями подключение библиотек обычно осуществляется через ключевое…
🙏11👍8🔥4🎉1
Навигация через диплинки
В некоторых приложениях навигация между экранами осуществляется через диплинки, а не хардкодится в приложении xml-графом или просто кодом
Теоретические описание:
открываешь ссылку — открывается страница в приложении
Практическое описание:
в манифесте описываются диплинки, которые собирается перехватывать приложение
через Intent их получает Activity и выбирает Fragment, который нужно открыть
Плюсы подхода:
• гибкость
не нужна хардкодить, какой экран открывает кнопка. можно прислать с бека диплинк, который будет открываться по нажатию кнопки
• возможность открывать сразу нужную страницу приложения из других приложений
например, отправить диплинк пользователю в смс или зашить в рекламный баннер
• можно на беке строить актуальный граф экранов
• можно передать параметры в диплинке
например, "sheme://testPage?couponCode=123"
удобно для аналитики маркетинга и передачи параметров с экрана на экран/устройства на устройство
• из коробки получаем открытие любого экрана по QR-коду
Минусы:
• нужно следить за тем, чтобы на некоторые экраны были доступ только из своего приложения
я бы не хотел, чтобы мошенник мог прислать диплинк на экран "автоматически отправить все фотографии с устройства на номер..."
• нужно следить за тем, что Activity возвращает через onActivityResult
другое приложение может открыть диплинк, а после получить секретные данные в onActivityResult
• бывают сложные сценарии
например, некоторые экраны должны открываться только с предыдущего экрана, а не из любой точки приложения
например, страница ввода кода из смс для авторизации
• никто не запретит другим приложениям-мошенникам перехватить диплинк
пользователь увидит диалог "через какое приложение открыть ссылку?" и выберет не вас
В некоторых приложениях навигация между экранами осуществляется через диплинки, а не хардкодится в приложении xml-графом или просто кодом
Теоретические описание:
открываешь ссылку — открывается страница в приложении
Практическое описание:
в манифесте описываются диплинки, которые собирается перехватывать приложение
через Intent их получает Activity и выбирает Fragment, который нужно открыть
Плюсы подхода:
• гибкость
не нужна хардкодить, какой экран открывает кнопка. можно прислать с бека диплинк, который будет открываться по нажатию кнопки
• возможность открывать сразу нужную страницу приложения из других приложений
например, отправить диплинк пользователю в смс или зашить в рекламный баннер
• можно на беке строить актуальный граф экранов
• можно передать параметры в диплинке
например, "sheme://testPage?couponCode=123"
удобно для аналитики маркетинга и передачи параметров с экрана на экран/устройства на устройство
• из коробки получаем открытие любого экрана по QR-коду
Минусы:
• нужно следить за тем, чтобы на некоторые экраны были доступ только из своего приложения
я бы не хотел, чтобы мошенник мог прислать диплинк на экран "автоматически отправить все фотографии с устройства на номер..."
• нужно следить за тем, что Activity возвращает через onActivityResult
другое приложение может открыть диплинк, а после получить секретные данные в onActivityResult
• бывают сложные сценарии
например, некоторые экраны должны открываться только с предыдущего экрана, а не из любой точки приложения
например, страница ввода кода из смс для авторизации
• никто не запретит другим приложениям-мошенникам перехватить диплинк
пользователь увидит диалог "через какое приложение открыть ссылку?" и выберет не вас
😎15🔥7👏6🦄5🤷♂1👍1
SmartTV — это Android?
Меня долго мучал этот вопрос
Вскрытие показало, что существует несколько видов прошивок:
• AndroidTV — практически тот же самый Android
но с переделанным лаунчером и урезанным функционалом
причем урезано даже приложение "Настройки", из-за чего нельзя, например, установить сертификат или переключить темную тему на светлую. приходится осваивать adb
• GoogleTV — практически тот же самый AndroidTV
только красивее
и AndroidTV, и GoogleTV разрабатывает Google
• кастомный AndroidTV — практически тот же самый AndroidTV, но с переделанным лаунчером и чем-нибудь еще
сюда отношу различные прошивки, которые появились с помощью небольшой кастомизации AndroidTV
компании могут взять образ системы и изменить в нем UI, добавить функционал, опмизировать, вставить рекламы
• SmartTV — а вот тут уже интереснее
SmartTV может быть как на базе Android, так и нет
например, SmartTV от Samsung использует их собственную Tizen OS, которая основана на Linux, а не Android
если вы захотите разработать что-то под Tizen OS, то придется скачать специальную Tizen Studio и освоить .NET/JS+HTML+CSS/С++
то есть поставить .apk на Samsung SmartTV не получится
аналогичная ситуация со SmartTV от LG — они прошивают телевизоры WebOS (Linux based)
Меня долго мучал этот вопрос
Вскрытие показало, что существует несколько видов прошивок:
• AndroidTV — практически тот же самый Android
но с переделанным лаунчером и урезанным функционалом
причем урезано даже приложение "Настройки", из-за чего нельзя, например, установить сертификат или переключить темную тему на светлую. приходится осваивать adb
• GoogleTV — практически тот же самый AndroidTV
только красивее
и AndroidTV, и GoogleTV разрабатывает Google
• кастомный AndroidTV — практически тот же самый AndroidTV, но с переделанным лаунчером и чем-нибудь еще
сюда отношу различные прошивки, которые появились с помощью небольшой кастомизации AndroidTV
компании могут взять образ системы и изменить в нем UI, добавить функционал, опмизировать, вставить рекламы
• SmartTV — а вот тут уже интереснее
SmartTV может быть как на базе Android, так и нет
например, SmartTV от Samsung использует их собственную Tizen OS, которая основана на Linux, а не Android
если вы захотите разработать что-то под Tizen OS, то придется скачать специальную Tizen Studio и освоить .NET/JS+HTML+CSS/С++
то есть поставить .apk на Samsung SmartTV не получится
аналогичная ситуация со SmartTV от LG — они прошивают телевизоры WebOS (Linux based)
🐳17👍15🥴10👌2🤡2
Один аккаунт на два приложения
Авторизовался в приложении А, а потом открыл приложение Б
А там окошко "Хотите войти под тем же аккаунтом?"
Как?
Ответ: через посредника == системный сервис == AccountManager
Что произошло под капотом:
1. Приложение А получило ваш логин и пароль
2, Приложение А проверило через бекенд, что вы существуете
3, Приложение А обратилось к системному AccoutManager, создало в нем Account и положило в него ваш логин и пароль
4. Приложение Б в момент запуска тоже обратилось к AccountManager, вязало Account и увидела ваш логин и пароль
Готово, приложение Б может авторизовать вас под тем же аккаунтом, не заставляя вспоминать пароль
Схема начинает выглядеть более безопасно, если учесть два факта:
• Account можно передавать только между приложениями, имеющими одну подпись
• прихранивать логин и пароль в сыром виде не стоит. вместо этого можно передавать обезличенный токен, зашифрованный на бекенде
Почему вам это надо:
• пользователю не нужно вспоминать пароль == увеличивается шанс, что он авторизуется
• компании не надо тратить деньги на СМС с кодом авторизации
Авторизовался в приложении А, а потом открыл приложение Б
А там окошко "Хотите войти под тем же аккаунтом?"
Как?
Ответ: через посредника == системный сервис == AccountManager
Что произошло под капотом:
1. Приложение А получило ваш логин и пароль
2, Приложение А проверило через бекенд, что вы существуете
3, Приложение А обратилось к системному AccoutManager, создало в нем Account и положило в него ваш логин и пароль
4. Приложение Б в момент запуска тоже обратилось к AccountManager, вязало Account и увидела ваш логин и пароль
Готово, приложение Б может авторизовать вас под тем же аккаунтом, не заставляя вспоминать пароль
Схема начинает выглядеть более безопасно, если учесть два факта:
• прихранивать логин и пароль в сыром виде не стоит. вместо этого можно передавать обезличенный токен, зашифрованный на бекенде
• компании не надо тратить деньги на СМС с кодом авторизации
🔥32👍7🕊3🐳1