launch
, async
/await
, …
suspend fun answer() = async {
delay(1000) // Heavy calculation, seriously
42
}
suspend fun main() {
println(answer().await())
}
fun main(/*args: Array<String>*/) = runBlocking {
}
suspend fun main(/*args: Array<String>*/) {
}
Suspend fun
dokud to jde. Až to nejde (override fun
), tak builder.
Nebo když jste si opravdu jistí, že se má něco provádět paralelně.
Nebo když prostě víte co děláte…
Všechno Default
(CPU), co nejméně si odskakovat do Main
(UI) a IO
.
launch(/*Dispatchers.Default*/) {
val data = withContext(Dispatchers.IO) {
// Read something from disc or internet
}
val processed = process(data) // Back on Default dispatcher
withContext(Dispatchers.Main) {
// Show result in UI
}
}
Default
IO
Default
a IO
sdílejí pool, nedochází ke context switchům.IO
SharedPreferences
při čtení sahá na disk! (~ 80 ms)Cokoliv má životní cyklus (Closeable
), mělo by mít vlastní scope.
class Foo :
Closeable,
CoroutineScope by CoroutineScope(Dispatchers.Default) {
override fun close() {
cancel() // Scope
}
}
GlobalScope
= ZLO!
Běží na main
vlákně!
class MyActivity : Activity(), CoroutineScope by MainScope() {
override fun onDestroy() {
super.onDestroy()
cancel() // Scope
}
}
class MyFragment : Fragment(), CoroutineScope by MainScope() {
override fun onDetach() {
super.onDetach()
cancel() // Scope
}
}
Běží na main
vlákně!
dependencies {
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:+'
}
class MyFragment : Fragment() { // Activity, Service
fun foo() {
// Attach/detach
lifecycleScope.launch {}
// viewCreated/destroyed
viewLifecycleOwner.lifecycleScope.launch {}
}
}
dependencies {
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:+'
}
class MyViewModel : ViewModel() {
fun foo() {
viewModelScope.launch {
// Main
}
}
}
launch { // ①
launch { // ②
// Stuff
}
launch { // ③
throw Exception()
}
}
CancellationException
).async
si drží výjimku až do await()
.
try {
launch {
}
}
launch {
try {
}
}
val job = launch {
launch {
// Stuff
}
launch {
// Stuff
}
}
job.cancel()
//job.cancelChildren()
launch(job) {
// Nope!
}
Job
už nemůže mít žádné potomky!Výjimka v potomkovi neukončí rodiče.
val supervisor = SupervisorJob()
launch(supervisor) {
// Stuff
}
launch(supervisor) {
throw Exception() // Everything is fine.
}
Možnost vlastního zpracování výjimek.
launch {
while(true) {
// Endless, ignores cancellation!
}
}
launch {
while(isActive) {
// Ends with cancellation.
}
}
val job1 = Job()
val job2 = Job()
launch(job1 + job2) {
// Stuff
}
job2.cancel()
job1.isActive // true
cancel
jednoho z rodiču zruší i závislé joby.
suspend fun OkHttpClient.coEnqueue(request: Request) =
suspendCancellableCoroutine<Response> { cont ->
newCall(request).apply {
cont.invokeOnCancellation { cancel() }
enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) =
cont.resume(response)
override fun onFailure(call: Call, e: IOException) =
cont.resumeWithException(e)
})
}
}
val data = try {
http.coEnqueue(request).body.string()
} catch (ioe: IOException) {
null
}
Channel s logikou
val actor = actor<Int>(capacity = Channel.CONFLATED) {
consumeEach {
delay(1000)
println(it)
}
}
actor.offer(1)
actor.offer(2)
actor.offer(3)
1
3
import kotlinx.coroutines.*
class App : Application() {
init {
System.setProperty(
DEBUG_PROPERTY_NAME,
DEBUG_PROPERTY_VALUE_ON
)
}
}
launch(Dispatchers.Main + CoroutineName("foobar")) {
println("${Thread.currentThread().name}\tHello")
}
main @foobar#8 Hello
interface StuffController {
@GET("stuff")
suspend fun getStuff(): Stuff
}
implementation 'androidx.room:room-coroutines:2.+'
@Dao
interface UsersDao {
@Query("SELECT * FROM users")
suspend fun getUsers(): List<User>
}
coEvery { foo.bar() } returns 42
coVerify { foo.bar() }
cancel()
v launch
.suspend
funkce.actor
na UI události.