Преобразование слушателей java в поток kotlin

Я разрабатываю на Android и в моем исходном коде много слушателей на основе обратного вызова. Я хочу преобразовать часть из них в поток kotlin. Я читал много статей, где говорится о callbackFlow, но не думаю, что это правильный выбор.

В моем случае что-то вроде этого:

interface ActionListener{
   public void actionStarted(actionId:Int)
   public void actionProgress(actionId:Int, elementCreated:ExampleElement)
   public void actionEnd(actionId:Int)
}

class MainProductor{
   ActionListener actionListener;
   
   MainProductor(ActionListener actionListener){
    this.actionListener = actionListener
   }
   
   void start(){
        //start some heavy works that start to call actionListener
   }
}

Вариант использования: когда я вызываю start, начинается какое-то действие с определенным идентификатором и начинается создание некоторого ExampleElement В пользовательском интерфейсе я должен показать список ExampleElement, созданный действиями с идентификатором A и с идентификатором B.

В пользовательском интерфейсе я должен показать прогресс, когда запускается одно из обоих действий и пока не будут созданы все элементы, созданные двумя идентификаторами действия, (эта часть очень сложная)

как можно продолжать использовать этот ActionListener и адаптировать его к новому потоку kotlin? Возможен способ?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
47
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

 interface ActionListener {
        fun actionStarted(actionId: Int)
        fun actionProgress(actionId: Int, progress: Float, elementCreated: ExampleElement)
        fun actionEnd(actionId: Int)
 }

 class MainProductor(val actionListener: ActionListener) {
        fun start() {
            //start some heavy works that start to call actionListener
        }
 }

Что ты пытаешься мне сказать? :D Хорошо, вы преобразовали код в kotlin, но это не моя «проблема».

aeroxr1 24.03.2022 12:15
Ответ принят как подходящий

Суть использования callbackFlow заключается в том, что он автоматически регистрирует и отменяет регистрацию слушателя при запуске/отмене. Возможным решением для работы с несколькими методами обратного вызова было бы создание соответствующих классов событий и их генерация.

sealed interface ActionListenerEvent {
    class Started(val actionId: Int) : ActionListenerEvent
    class ProgressChanged(val actionId: Int, val progress: Float, val elementCreated: ExampleElement) : ActionListenerEvent
    class Finished(val actionId: Int) : ActionListenerEvent
}


fun actionEvents() = callbackFlow {
    val listener = object : ActionListener {
        override fun actionStarted(actionId: Int) {
            trySend(ActionListenerEvent.Started(actionId))
        }

        override fun actionProgress(actionId: Int, progress: Float, elementCreated: ExampleElement) {
            trySend(ActionListenerEvent.ProgressChanged(actionId, progress, elementCreated))
        }

        override fun actionEnd(actionId: Int) {
            trySend(ActionListenerEvent.Finished(actionId))
        }
    }

    registerListener(listener)

    awaitClose {
        unregisterListener(listener)
    }
}

Если вам нужно решение, которое лучше контролирует, когда ваш слушатель зарегистрирован/не зарегистрирован, вы также можете использовать MutableSharedFlow для отображения событий.

class MainProductor{
    val events = MutableSharedFlow<ActionListenerEvent>() // may need to configure replay and buffer depending on your specific use case
    private val listener  = object : ActionListener {
        override fun actionStarted(actionId: Int) {
            events.tryEmit(ActionListenerEvent.Started(actionId))
        }

        override fun actionProgress(actionId: Int, progress: Float, elementCreated: ExampleElement) {
            events.tryEmit(ActionListenerEvent.ProgressChanged(actionId, progress, elementCreated))
        }

        override fun actionEnd(actionId: Int) {
            events.tryEmit(ActionListenerEvent.Finished(actionId))
        }
    }
    
    fun start(){
        registerListener(this.listener)
    }
    
    fun stop(){
        unregisterListener(this.listener)
    }
}

Обновлено: пример использования

Хотя я не совсем уверен в ожидаемом поведении, особенно в том, что делать с частью elementCreated: ExampleElement, я предполагаю, что вы хотите отобразить список текущих действий с их соответствующим прогрессом. Объединение событий в такой список может выглядеть примерно так:

data class RunningAction(val id: Int, val progress: Float)// TODO integrate "elementCreated"

fun collectEvents() = flow {
    val runningActions = mutableMapOf<Int, RunningAction>()
    actionEvents().collect { event ->
        when (event) {
            is ActionListenerEvent.Started -> {
                runningActions[event.actionId] = RunningAction(id = event.actionId, progress = 0f)
            }
            is ActionListenerEvent.ProgressChanged -> {
                val action = runningActions[event.actionId]
                if (action != null){
                    runningActions[event.actionId] = action.copy(progress = event.progress) // TODO integrate "elementCreated"
                }
            }
            is ActionListenerEvent.Finished -> {
                runningActions.remove(event.actionId)
            }
        }
        emit(runningActions.values)
    }
}

Спасибо. попробую вашу идею! Используя вашу идею, если из модели представления я хочу собирать события и показывать все элементы двух разных идентификаторов действий, как я могу это сделать? Какой оператор я могу использовать на нем?

aeroxr1 24.03.2022 12:43

@aeroxr1 Aeroxr1 Я добавил возможную реализацию для объединения событий, не уверен, что она полностью соответствует ожидаемому поведению, но вы сможете использовать ее в качестве основы.

Adrian K 24.03.2022 13:04

Я добавил вариант использования в вопрос :)

aeroxr1 24.03.2022 14:55

Другие вопросы по теме