Найти в Дзене
​​Многопоточность за 100 слов Проблема: есть объект запускаем два потока у каждого потока свой кэш, то есть каждый поток создает копию этого объекта, а не работает с ним напрямую дальше по шагам: 1) первый поток скопировал объект к себе в кэш 2) первый поток изменил поле объекта 3) второй поток скопировал объект к себе в кэш 4) второй поток изменяет это же поле (и не знает об изменениях, сделанных первым потоком!) 5) второй поток записывает изменения в общую память 6) первый поток записывает изменения в общую память бац — мы потеряли изменения, сделанные во втором потоке. первый поток их перезаписал + второй поток даже не знал, что объект был изменен в первом потоке! Как этого избежать: • synchronized — запрещает нескольким потокам работать с объектом (классом) одновременно перед тем как выполнить участок кода, поток захватывает объект (класс), а другие потоки ждут освобождения объекта (класса) - synchronized(Object) — захватывает объект - synchronized fun — можно читать как synchronized(this), захватывает объект - static synchronized fun — захватывает класс (а не объект) • volatile — синхронизирует чтение и запись сработает, если один поток только читает, а другой только пишет не сработает, если оба потока изменяют объект — потому что операции между чтением и записью не синхронизированы • atomic атомарная операция = неделимая операция изменения объекта с объектами, имеющими атомарные операции изменения (например, увеличение на единицу для AtomicInteger), можно работать в разных потоках без ручной синхронизации в Java есть атомарные примитивы, коллекции и класс AtomicReference для работы с произвольными классами .... а нужно ли думать о синхронизации переменной в корутинах? сработают ли там эти подходы?
3 года назад
​​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, библиотек или личного опыта, когда использование вложенных или внутренних классов было действительно оправдано?
3 года назад
​​Почему нельзя писать 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 уже уничтожено — приложение упадет
3 года назад
​​Kotlin Flow: StateFlow vs SharedFlow Часть №2 Общее для StateFlow и SharedFlow: • это горячие потоки то есть вызов collect() лишь позволяет собрать данные, которые уже в них есть или поступят в них, но не запускает выполнение какого-либо билдера (как это происходит с холодными потоками) • они не отслеживает жизненный цикл активити\фрагмента\вьюшки но их можно запустить в корутинах, которые автоматически остановятся во время вызова onStop(). остановка \корутины вызывает остановку выполнения collect() • при создании нужно указать начальное значение Отличия 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
3 года назад
​​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()
3 года назад
Если нравится — подпишитесь
Так вы не пропустите новые публикации этого канала