/
CoroutineAndroidLoader.kt
61 lines (52 loc) · 2.13 KB
/
CoroutineAndroidLoader.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package se.hellsoft.kotlinasyncwithcoroutines
import android.arch.lifecycle.Lifecycle
import android.arch.lifecycle.LifecycleObserver
import android.arch.lifecycle.LifecycleOwner
import android.arch.lifecycle.OnLifecycleEvent
import android.util.Log
import kotlinx.coroutines.experimental.CoroutineStart
import kotlinx.coroutines.experimental.Deferred
import kotlinx.coroutines.experimental.Job
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.newFixedThreadPoolContext
// Quick & dirty logcat extensions
inline fun <reified T> T.logd(message: () -> String) = Log.d(T::class.simpleName, message())
inline fun <reified T> T.loge(error: Throwable, message: () -> String) = Log.d(T::class.simpleName, message(), error)
internal class CoroutineLifecycleListener(private val deferred: Deferred<*>) : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun cancelCoroutine() {
if (!deferred.isCancelled) {
deferred.cancel()
}
}
}
// CoroutineContext running on background threads.
internal val Background = newFixedThreadPoolContext(Runtime.getRuntime().availableProcessors() * 2, "Loader")
/**
* Creates a lazily started coroutine that runs <code>loader()</code>.
* The coroutine is automatically cancelled using the CoroutineLifecycleListener.
*/
fun <T> LifecycleOwner.load(loader: suspend () -> T): Deferred<T> {
val deferred = async(context = Background, start = CoroutineStart.LAZY) {
loader()
}
lifecycle.addObserver(CoroutineLifecycleListener(deferred))
return deferred
}
/**
* Extension function on <code>Deferred<T><code> that creates a launches a coroutine which
* will call <code>await()</code> and pass the returned value to <code>block()</code>.
*/
infix fun <T> Deferred<T>.then(block: suspend (T) -> Unit): Job {
return launch(context = UI) {
try {
block(this@then.await())
} catch (e: Exception) {
// Just log the exception to confirm when we get cancelled (Expect JobCancellationException)
loge(e) { "Exception in then()!" }
throw e
}
}
}