Вызов функции приостановки Kotlin в классе Java

Предположим, у нас есть следующая функция приостановки:

suspend fun doSomething(): List<MyClass> { ... }

Если я хочу вызвать эту функцию в одном из моих существующих классов Java (который я пока не могу преобразовать в Kotlin) и получить ее возвращаемое значение, я должен указать Continuation<? super List<MyClass>> в качестве ее параметра (очевидно).

Мой вопрос: как я могу его реализовать. Особенно его getContext геттер.


person mrtaikandi    schedule 18.10.2018    source источник
comment
Я бы сделал все возможное, чтобы этого не случилось; Я бы не ожидал, что это сработает очень эффективно. Например, вы можете добавить еще одну функцию Kotlin для запуска сопрограммы, как вы считаете целесообразным, это не развлечение с приостановкой, и вызвать ее из Java.   -  person Louis Wasserman    schedule 18.10.2018
comment
Раньше у меня был код Java, в котором удалось создать реализацию Continuation и вызвать suspend fun , но в Kotlin 1.3 Continuation объявляет resumeWith(Result), где Result - это размеченное объединение результата и internal class Failure, и нет никакого способа предоставить это из Java, за исключением использования отражения для доступа к частным членам в реализации Kotlin.   -  person Marko Topolnik    schedule 19.10.2018


Ответы (3)


Сначала добавьте модуль org.jetbrains.kotlinx:kotlinx-coroutines-jdk8 в свои зависимости. В вашем файле Kotlin определите следующую асинхронную функцию, которая соответствует стилю Java для написания асинхронных API:

fun doSomethingAsync(): CompletableFuture<List<MyClass>> =
    GlobalScope.future { doSomething() }

Теперь используйте doSomethingAsync из Java так же, как вы используете другие асинхронные API в мире Java.

person Roman Elizarov    schedule 19.10.2018
comment
почему это не помечено как ответ ?? работал как шарм. - person Oreoluwa; 04.11.2018
comment
Просто к сведению для начинающих пользователей Gradle (таких как я) - вам нужно добавить версию в зависимость, например: implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.0.1" - person Wolfgang Schreurs; 08.12.2018
comment
Можно ли вызвать из java следующий код kotlin: GlobalScope.launch (Dispatchers.Main) ...? - person Alex; 12.07.2019
comment
Не работает на Android 6.0. Ошибка: java.lang.NoClassDefFoundError: Ошибка разрешения: Ljava / util / concurrent / CompletableFuture; - person Alex; 15.07.2019
comment
Вы представляете его как любой другой будущий тип и используете в своем Java-коде. У нас есть встроенная поддержка преобразования в CompletableFuture, ListenableFuture, Rx Single, Reactor Mono. Вы также можете написать свой собственный. - person Roman Elizarov; 16.07.2019
comment
Сработало отлично. Моя единственная проблема заключается в том, как протестировать его в kotlin с помощью mockito2, проверка никогда не вызывается: / - person Nil Oleart; 28.08.2019
comment
Примечание: для этого требуется уровень API 24 или выше. - person bartonstanley; 12.10.2019
comment
проверьте мой ответ для каждого API Android - person Dragos Rachieru; 11.11.2019
comment
используйте jdk7 для старых API - person Dragos Rachieru; 25.11.2019
comment
Теперь используйте doSomethingAsync из Java так же, как вы используете другие асинхронные API в мире Java. Как??? - person Barry Fruitman; 08.12.2020
comment
использование Dispatchers.Main обычно требуется при вызове из java: fun doSomethingAsync (): CompletableFuture ‹List ‹MyClass›› = GlobalScope.future (Dispatchers.Main) {doSomething ()} - person SteelBytes; 07.03.2021

Если вы не хотите использовать org.jetbrains.kotlinx:kotlinx-coroutines-jdk8, у меня есть новая идея.

Напишите ниже код в своем проекте kotlin.

    @JvmOverloads
    fun <R> getContinuation(onFinished: BiConsumer<R?, Throwable?>, dispatcher: CoroutineDispatcher = Dispatchers.Default): Continuation<R> {
        return object : Continuation<R> {
            override val context: CoroutineContext
                get() = dispatcher

            override fun resumeWith(result: Result<R>) {
                onFinished.accept(result.getOrNull(), result.exceptionOrNull())
            }
        }
    }

Я пишу это в своем Coroutines классе

Затем вы можете вызвать функцию приостановки, например:

            Coroutines coroutines = new Coroutines();
            UserUtils.INSTANCE.login("user", "pass", coroutines.getContinuation(
                    (tokenResult, throwable) -> {
                        System.out.println("Coroutines finished");
                        System.out.println("Result: " + tokenResult);
                        System.out.println("Exception: " + throwable);
                    }
            ));

Функция login () является функцией приостановки.
suspend fun login(username: String, password: String): TokenResult

Для вашего кода вы можете:

doSomething(getContinuation((result, throwable) -> { 
       //TODO
}));
person Kenvix Zure    schedule 10.03.2020
comment
классно!!! это обходной путь для вызова сопрограммы внутри java для взаимодействия - person mochadwi; 06.05.2020
comment
Как и ответ Романа, для этого также требуется API 24 или выше. - person Samuel; 05.05.2021
comment
@Samuel Может быть, вы можете заменить BiConsumer и Lambda своим собственным интерфейсом и анонимным классом, чтобы сделать его совместимым со старым Android SDK (не тестировалось) - person Kenvix Zure; 22.06.2021

Для сопрограмм 1.3.0 используйте это:

BuildersKt.launch(GlobalScope.INSTANCE,
                Dispatchers.getMain(),//context to be ran on
                CoroutineStart.DEFAULT,
                (coroutineScope, continuation) -> suspendFunction(arguments)
        );

Для java ‹8:

BuildersKt.launch(
        GlobalScope.INSTANCE,
        Dispatchers.getMain(),//context to be ran on
        CoroutineStart.DEFAULT,
        new Function2<CoroutineScope, Continuation<? super Unit>, Unit/*or your return type here*/>() {
            @Override
            public Unit/*or your return type here*/ invoke(CoroutineScope coroutineScope, Continuation<? super Unit> continuation) {
                //do what you want
                return Unit.INSTANCE; //or something with the defined type
            }
        }
);

Мой файл gradle:

implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"

Kotlin использует статические классы для функций расширения, запуск - это функция расширения, поэтому он определен в BuildersKt. Первый параметр - это цель функции расширения, остальные - параметры функций расширения.

person Dragos Rachieru    schedule 11.11.2019
comment
Этот подход бесполезен, потому что вы не можете возобновить продолжение, он требует встроенного класса, к которому вы не можете получить доступ из Java. - person Marko Topolnik; 04.02.2020
comment
Я не понимаю о чем ты. Если вы хотите использовать Deferred, вы можете использовать BuildersKt.async вместо запуска, так работают сопрограммы. - person Dragos Rachieru; 13.02.2020
comment
// do what you want - вы не можете делать здесь то, что хотите, потому что вы должны предоставить продолжение, которое ожидает возобновления с типом возвращаемого значения вызываемой функции, но у вас нет такого продолжения, и ваш ответ не показывает, как построить его из Java. Продолжение, которое вы получаете от launch, является просто продолжением завершения, которое вы вызываете с помощью Unit, когда вся сопрограмма завершена. - person Marko Topolnik; 13.02.2020