Kotlin& Anko协程:在异步外返回

时间:2017-09-13 12:04:14

标签: android kotlin anko

在我们的项目中,我们需要在执行操作之前获取一些数据(将被存储)。如果数据是在15分钟前获得的,我们必须刷新它。

为了做到这一点,我们正在使用Kotlin + Anko协同程序。这个想法(假设数据是在之前的某个时刻获得的)是:

调用该方法并在获取数据时进行检查。 如果它不到15分钟前,返回它。 如果没有,则异步获取它(这是一个网络操作),存储它并返回它。

由于在获取刷新数据之前我们无法执行任何操作,因此刷新必须是同步的(通过网络操作本身是异步的)。

我们有这段代码:

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)
            return@async obtainedData
        }

 // ???????

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

我们如何使函数等待异步(UI)块之外的结果?这种回归是必要的,但我们不知道应该把它放在那里。我们尝试使用Deferred对象的getCompleted()方法(返回asyncUser.getCompleted()),但它最终崩溃,因为它返回null。

谢谢!

1 个答案:

答案 0 :(得分:3)

我看到了几种方法。一种是使用kotlinx.coroutines.experimental方法runBlocking

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

        val obtainedData = storeAndRetrieve(obtainData.await(), context)
        return@async obtainedData
    }
    return@runBlocking asyncUser.await()
}

根据使用情况,您甚至可以将其简化为:

val asyncUser = runBlocking {
    async(CommonPool) {
        val obtainedData = storeAndRetrieve(api?.obtainData(sphelper.getDefaultUser(context)), context)
        return@async obtainedData
    }.await()
}

另一种方法是使用基本的Java CountDownLatch

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()