83 votes

Appeler la fonction de suspension Kotlin dans une classe Java

Supposons que nous ayons la fonction de suspension suivante :

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

Si je veux appeler cette fonction dans l'une de mes classes Java existantes (que je ne suis pas en mesure de convertir en Kotlin pour l'instant) et obtenir sa valeur de retour, je dois fournir un fichier de type Continuation<? super List<MyClass>> comme son paramètre (évidemment).

Ma question est la suivante : comment puis-je en mettre un en place ? Spécialement son getContext getter.

1 votes

Je ferais tout mon possible pour éviter d'avoir à le faire ; je ne m'attendrais pas à ce que cela fonctionne très efficacement. Par exemple, vous pourriez ajouter une autre fonction Kotlin pour lancer la coroutine de la manière que vous jugez appropriée, qui n'est pas une fonction de suspension, et l'appeler depuis Java.

1 votes

J'avais l'habitude de Code Java qui a réussi à créer une implémentation de Continuation et appeler un suspend fun mais dans Kotlin 1.3 Continuation déclare resumeWith(Result) , donde Result est une union discriminée du résultat et d'une internal class Failure et il n'y a aucun moyen de fournir cela à partir de Java, sauf en utilisant la réflexion pour accéder aux membres privés dans l'implémentation Kotlin.

95voto

Roman Elizarov Points 8871

D'abord, ajoutez org.jetbrains.kotlinx:kotlinx-coroutines-jdk8 à vos dépendances. Dans votre fichier Kotlin, définissez la fonction asynchrone suivante qui correspond au style Java d'écriture des API asynchrones :

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

Maintenant, utilisez doSomethingAsync de Java de la même manière que vous utilisez d'autres API asynchrones dans le monde Java.

0 votes

Pourquoi n'est-ce pas marqué comme la réponse ?? ça a marché comme un charme.

16 votes

Juste un FYI pour les utilisateurs débutants de Gradle (comme moi) - vous devez ajouter la version à la dépendance, par exemple : implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.0.1"

0 votes

Est-il possible d'appeler depuis java le code kotlin suivant : GlobalScope.launch(Dispatchers.Main)... ?

16voto

Kenvix Zure Points 71

Si vous ne voulez pas utiliser org.jetbrains.kotlinx:kotlinx-coroutines-jdk8 J'ai une nouvelle idée.

Ecrivez le code ci-dessous dans votre projet 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())
            }
        }
    }

Je l'écris dans mon Coroutines classe

Ensuite, vous pouvez appeler votre fonction de suspension comme :

            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);
                    }
            ));

La fonction login() est une fonction de suspension.
suspend fun login(username: String, password: String): TokenResult

Pour votre code, vous pouvez :

doSomething(getContinuation((result, throwable) -> { 
       //TODO
}));

9voto

Dragos Rachieru Points 350

Pour les coroutines 1.3.0, utilisez ceci :

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

Pour 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
            }
        }
);

Mon fichier 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 utilise des classes statiques pour les fonctions d'extension, launch est une fonction d'extension, elle est donc définie dans BuildersKt. Le premier paramètre est la cible de la fonction d'extension, les autres sont les paramètres des fonctions d'extension.

0 votes

Cette approche est inutile car vous ne pouvez pas reprendre la continuation, elle prend une classe inline à laquelle vous ne pouvez pas accéder depuis Java.

0 votes

Je ne comprends pas ce que vous voulez dire. Si vous voulez utiliser Deferred, vous pouvez utiliser BuildersKt.async au lieu de launch, c'est ainsi que fonctionnent les coroutines.

6 votes

// do what you want -- Vous ne pouvez pas faire ce que vous voulez ici car vous devez fournir une continuation qui s'attend à être reprise avec le type de retour de la fonction appelée, mais vous n'avez pas une telle continuation et votre réponse ne montre pas comment la construire à partir de Java. La continuation que vous obtenez de launch est juste la continuation de l'achèvement, celle que vous invoquez avec Unit quand la coroutine entière est terminée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X