2017-09-13 5 views
0

Dans notre projet, nous devons obtenir des données (qui seront stockées) avant de faire des opérations. Dans le cas où les données ont été obtenues il y a plus de 15 minutes, nous devons les actualiser.Corotines Kotlin & Anko: retour en dehors async

Nous utilisons des coroutines Kotlin + Anko pour ce faire. L'idée (en supposant que les données aient été obtenues à un moment donné) est la suivante:

La méthode est appelée et vérifie quand nous avons les données. Si c'était moins de 15 minutes, le retourne. Si ce n'est pas le cas, procurez-vous-le de manière asynchrone (c'est une opération de réseau), stockez-le et renvoyez-le. Puisque nous ne pouvons rien faire avant d'obtenir les données actualisées, l'actualisation doit être synchrone (bien que l'opération réseau soit elle-même asynchrone).

Nous avons ce code:

fun Context.retrieveInfo(api: Api?): User? { 

    try { 

    // For sake of simplification the real conditional check is removed 
    if (time > 15) { 

     val context = this 
     val asyncUser = async(UI) { 
      val obtainData: Deferred<Data?> = bg { 
       api?.obtainData(sphelper.getDefaultUser(context)) 
      } 

      val obtainedData = storeAndRetrieve(obtainData.await(), context) 
      [email protected] obtainedData 
     } 

// ??????? 

    } else { 
     val input = ObjectInputStream(this.openFileInput("data.dat")) 
     return input.readObject() as Data 
    } 
    } catch (e: Exception) { 
    return null 
    } 
} 

Comment pouvons-nous faire fonctionner attendre le résultat en dehors de la async (UI) bloquer? Ce retour est nécessaire, mais nous n'avons aucune idée de ce que nous devrions y mettre. Nous avons essayé avec la méthode getCompleted() de l'objet Deferred (return asyncUser.getCompleted()) mais il finit par s'écraser car il renvoie null.

Merci!

Répondre

0

Je vois un certain nombre de façons de le faire. La première consiste à utiliser la méthode kotlinx.coroutines.experimentalrunBlocking:

val user = runBlocking(CommonPool) { 
    val asyncUser = async(UI) { 
     val obtainData: Deferred<String> = bg { 
      ... 
     } 

     val obtainedData = storeAndRetrieve(obtainData.await(), context) 
     [email protected] obtainedData 
    } 
    [email protected] asyncUser.await() 
} 

Selon le cas d'utilisation, vous pouvez même être en mesure de simplifier à:

val asyncUser = runBlocking { 
    async(CommonPool) { 
     val obtainedData = storeAndRetrieve(api?.obtainData(sphelper.getDefaultUser(context)), context) 
     [email protected] obtainedData 
    }.await() 
} 

Une autre façon est d'utiliser Java CountDownLatch de base:

var asyncUser: String? = null 
val c = CountDownLatch(1) 
async(UI) { 
    val obtainData: Deferred<String> = bg { 
     ... 
    } 

    val obtainedData = obtainData.await() 
    asyncUser = obtainedData 
    c.countDown() 
} 
c.await() 
+0

J'ai essayé la version simplifiée et CountDownLatch et les deux fonctionnent très bien. Je vous remercie! –

+0

J'ai lu que runBlocking est à des fins de test, pas idéal pour les produits finis. –