0
# Asynchronous Tasks (Bolts)
1
2
The Bolts framework provides a powerful task management system for handling asynchronous operations with continuation support, error handling, and cancellation. It enables chaining of asynchronous operations and provides a clean alternative to nested callbacks.
3
4
## Task
5
6
The core Task class for managing asynchronous operations:
7
8
```kotlin { .api }
9
class Task<TResult> private constructor() {
10
// Task state
11
val isCompleted: Boolean
12
val isCancelled: Boolean
13
val isFaulted: Boolean
14
val result: TResult?
15
val error: Exception?
16
17
// Continuation methods
18
fun <TContinuationResult> continueWith(
19
continuation: Continuation<TResult, TContinuationResult>
20
): Task<TContinuationResult>
21
22
fun <TContinuationResult> continueWith(
23
continuation: Continuation<TResult, TContinuationResult>,
24
executor: Executor
25
): Task<TContinuationResult>
26
27
fun <TContinuationResult> continueWithTask(
28
continuation: Continuation<TResult, Task<TContinuationResult>>
29
): Task<TContinuationResult>
30
31
fun <TContinuationResult> continueWithTask(
32
continuation: Continuation<TResult, Task<TContinuationResult>>,
33
executor: Executor
34
): Task<TContinuationResult>
35
36
// Success continuations
37
fun <TContinuationResult> onSuccess(
38
continuation: Continuation<TResult, TContinuationResult>
39
): Task<TContinuationResult>
40
41
fun <TContinuationResult> onSuccess(
42
continuation: Continuation<TResult, TContinuationResult>,
43
executor: Executor
44
): Task<TContinuationResult>
45
46
fun <TContinuationResult> onSuccessTask(
47
continuation: Continuation<TResult, Task<TContinuationResult>>
48
): Task<TContinuationResult>
49
50
fun <TContinuationResult> onSuccessTask(
51
continuation: Continuation<TResult, Task<TContinuationResult>>,
52
executor: Executor
53
): Task<TContinuationResult>
54
55
// Wait methods
56
fun waitForCompletion()
57
fun waitForCompletion(duration: Long, timeUnit: TimeUnit): Boolean
58
59
companion object {
60
// Task creation
61
fun <TResult> call(callable: Callable<TResult>): Task<TResult>
62
fun <TResult> call(callable: Callable<TResult>, executor: Executor): Task<TResult>
63
fun <TResult> callInBackground(callable: Callable<TResult>): Task<TResult>
64
fun <TResult> forResult(result: TResult): Task<TResult>
65
fun <TResult> forError(error: Exception): Task<TResult>
66
fun <TResult> cancelled(): Task<TResult>
67
68
// Task composition
69
fun <TResult> whenAll(tasks: Collection<Task<TResult>>): Task<List<TResult>>
70
fun whenAllResult(tasks: Collection<Task<*>>): Task<Void>
71
fun <TResult> whenAny(tasks: Collection<Task<TResult>>): Task<Task<TResult>>
72
fun whenAnyResult(tasks: Collection<Task<*>>): Task<Task<*>>
73
74
// Delay
75
fun delay(millis: Long): Task<Void>
76
fun delay(millis: Long, cancellationToken: CancellationToken): Task<Void>
77
}
78
}
79
80
interface Continuation<TTaskResult, TContinuationResult> {
81
@Throws(Exception::class)
82
fun then(task: Task<TTaskResult>): TContinuationResult
83
}
84
```
85
86
### Basic Task Usage
87
88
```kotlin
89
class TaskExamples {
90
91
fun basicTaskUsage() {
92
// Create a task from a callable
93
val task = Task.call {
94
// Simulate some work
95
Thread.sleep(1000)
96
"Task completed successfully"
97
}
98
99
// Continue with the result
100
task.continueWith { task ->
101
if (task.isFaulted) {
102
Log.e("Task", "Task failed: ${task.error?.message}")
103
"Error occurred"
104
} else {
105
val result = task.result
106
Log.d("Task", "Task result: $result")
107
result?.uppercase()
108
}
109
}.continueWith { task ->
110
val finalResult = task.result
111
Log.d("Task", "Final result: $finalResult")
112
finalResult
113
}
114
}
115
116
fun backgroundTaskUsage() {
117
// Execute task in background
118
Task.callInBackground {
119
// Perform network request or database operation
120
fetchDataFromApi()
121
}.onSuccess { result ->
122
// This runs on the main thread by default
123
Log.d("Task", "Data fetched: $result")
124
updateUI(result)
125
}.continueWith { task ->
126
if (task.isFaulted) {
127
Log.e("Task", "Background task failed: ${task.error?.message}")
128
handleError(task.error)
129
}
130
null
131
}
132
}
133
134
fun chainedTasksExample() {
135
// Chain multiple asynchronous operations
136
Task.callInBackground {
137
authenticateUser()
138
}.onSuccessTask { authResult ->
139
// Return another task
140
Task.callInBackground {
141
fetchUserProfile(authResult.userId)
142
}
143
}.onSuccessTask { profile ->
144
// Another chained operation
145
Task.callInBackground {
146
loadUserPreferences(profile.id)
147
}
148
}.onSuccess { preferences ->
149
// Final success handler
150
Log.d("Task", "All operations completed")
151
setupUserSession(preferences)
152
}.continueWith { task ->
153
if (task.isFaulted) {
154
Log.e("Task", "Chain failed: ${task.error?.message}")
155
showLoginScreen()
156
}
157
null
158
}
159
}
160
}
161
```
162
163
## Task Completion Source
164
165
Manual control over task completion:
166
167
```kotlin { .api }
168
class TaskCompletionSource<TResult> {
169
val task: Task<TResult>
170
val isCancellationRequested: Boolean
171
172
fun setResult(result: TResult): Boolean
173
fun trySetResult(result: TResult): Boolean
174
fun setError(error: Exception): Boolean
175
fun trySetError(error: Exception): Boolean
176
fun setCancelled(): Boolean
177
fun trySetCancelled(): Boolean
178
}
179
```
180
181
### Task Completion Source Usage
182
183
```kotlin
184
class ManualTaskControl {
185
186
fun createManualTask(): Task<String> {
187
val tcs = TaskCompletionSource<String>()
188
189
// Simulate async operation with manual completion
190
Thread {
191
try {
192
Thread.sleep(2000)
193
// Manually complete the task
194
tcs.setResult("Manual task completed")
195
} catch (e: InterruptedException) {
196
tcs.setError(e)
197
}
198
}.start()
199
200
return tcs.task
201
}
202
203
fun createConditionalTask(condition: Boolean): Task<String> {
204
val tcs = TaskCompletionSource<String>()
205
206
if (condition) {
207
tcs.setResult("Condition met")
208
} else {
209
tcs.setError(IllegalStateException("Condition not met"))
210
}
211
212
return tcs.task
213
}
214
215
fun createTimeoutTask(timeoutMs: Long): Task<String> {
216
val tcs = TaskCompletionSource<String>()
217
218
// Set up timeout
219
Handler(Looper.getMainLooper()).postDelayed({
220
if (!tcs.task.isCompleted) {
221
tcs.setError(TimeoutException("Task timed out"))
222
}
223
}, timeoutMs)
224
225
// Simulate work
226
Thread {
227
try {
228
Thread.sleep(timeoutMs + 1000) // Will timeout
229
tcs.trySetResult("Work completed")
230
} catch (e: Exception) {
231
tcs.trySetError(e)
232
}
233
}.start()
234
235
return tcs.task
236
}
237
}
238
```
239
240
## Cancellation Support
241
242
Handle task cancellation with cancellation tokens:
243
244
```kotlin { .api }
245
class CancellationToken {
246
val isCancellationRequested: Boolean
247
248
fun throwIfCancellationRequested()
249
fun register(action: Runnable): CancellationTokenRegistration
250
}
251
252
class CancellationTokenSource {
253
val token: CancellationToken
254
val isCancellationRequested: Boolean
255
256
fun cancel()
257
fun cancelAfter(delayMs: Long)
258
fun close()
259
}
260
261
class CancellationTokenRegistration {
262
fun close()
263
}
264
```
265
266
### Cancellation Usage
267
268
```kotlin
269
class CancellationExamples {
270
271
fun cancellableTask() {
272
val cancellationSource = CancellationTokenSource()
273
val cancellationToken = cancellationSource.token
274
275
val task = Task.call({
276
// Long running operation
277
for (i in 1..100) {
278
// Check for cancellation
279
cancellationToken.throwIfCancellationRequested()
280
281
// Simulate work
282
Thread.sleep(100)
283
Log.d("Task", "Progress: $i/100")
284
}
285
"Task completed"
286
}, BoltsExecutors.background())
287
288
// Cancel after 5 seconds
289
Handler(Looper.getMainLooper()).postDelayed({
290
cancellationSource.cancel()
291
Log.d("Task", "Cancellation requested")
292
}, 5000)
293
294
task.continueWith { task ->
295
when {
296
task.isCancelled -> Log.d("Task", "Task was cancelled")
297
task.isFaulted -> Log.e("Task", "Task failed: ${task.error?.message}")
298
else -> Log.d("Task", "Task completed: ${task.result}")
299
}
300
null
301
}
302
}
303
304
fun cancellationWithCleanup() {
305
val cancellationSource = CancellationTokenSource()
306
307
// Register cleanup action
308
val registration = cancellationSource.token.register {
309
Log.d("Task", "Performing cleanup...")
310
cleanupResources()
311
}
312
313
val task = Task.callInBackground {
314
try {
315
performLongRunningOperation(cancellationSource.token)
316
} finally {
317
registration.close()
318
}
319
}
320
321
// Auto-cancel after timeout
322
cancellationSource.cancelAfter(10000) // 10 seconds
323
324
return task
325
}
326
}
327
```
328
329
## Task Executors
330
331
Control where tasks execute:
332
333
```kotlin { .api }
334
object BoltsExecutors {
335
fun background(): Executor
336
fun immediate(): Executor
337
}
338
339
object AndroidExecutors {
340
fun mainThread(): Executor
341
fun uiThread(): Executor
342
}
343
```
344
345
### Executor Usage
346
347
```kotlin
348
class ExecutorExamples {
349
350
fun executorControlExample() {
351
// Background task
352
Task.call({
353
// Heavy computation
354
performCpuIntensiveWork()
355
}, BoltsExecutors.background())
356
.continueWith({ task ->
357
// Process result on background thread
358
processResult(task.result)
359
}, BoltsExecutors.background())
360
.continueWith({ task ->
361
// Update UI on main thread
362
updateUI(task.result)
363
null
364
}, AndroidExecutors.mainThread())
365
}
366
367
fun immediateExecutorExample() {
368
// Task that executes immediately on current thread
369
val result = Task.forResult("immediate result")
370
371
result.continueWith({ task ->
372
Log.d("Task", "This runs immediately: ${task.result}")
373
null
374
}, BoltsExecutors.immediate())
375
}
376
}
377
```
378
379
## Task Composition
380
381
Combine multiple tasks:
382
383
```kotlin
384
class TaskComposition {
385
386
fun parallelTasksExample() {
387
val task1 = Task.callInBackground { fetchUserData() }
388
val task2 = Task.callInBackground { fetchUserPreferences() }
389
val task3 = Task.callInBackground { fetchUserSettings() }
390
391
// Wait for all tasks to complete
392
Task.whenAll(listOf(task1, task2, task3))
393
.onSuccess { results ->
394
val userData = results[0]
395
val preferences = results[1]
396
val settings = results[2]
397
398
// Combine all results
399
setupUserSession(userData, preferences, settings)
400
}
401
.continueWith { task ->
402
if (task.isFaulted) {
403
Log.e("Task", "One or more tasks failed: ${task.error?.message}")
404
handleError(task.error)
405
}
406
null
407
}
408
}
409
410
fun raceConditionExample() {
411
val cacheTask = Task.callInBackground { loadFromCache() }
412
val networkTask = Task.callInBackground { loadFromNetwork() }
413
414
// Use whichever completes first
415
Task.whenAny(listOf(cacheTask, networkTask))
416
.onSuccess { firstCompletedTask ->
417
val winningTask = firstCompletedTask
418
if (!winningTask.isFaulted) {
419
Log.d("Task", "Using result from fastest source")
420
processData(winningTask.result)
421
} else {
422
Log.w("Task", "Fastest task failed, waiting for other...")
423
// Handle the case where first task failed
424
}
425
}
426
}
427
428
fun conditionalChainingExample() {
429
Task.callInBackground {
430
checkUserAuthentication()
431
}.onSuccessTask { isAuthenticated ->
432
if (isAuthenticated) {
433
// User is authenticated, fetch protected data
434
Task.callInBackground { fetchProtectedUserData() }
435
} else {
436
// User not authenticated, redirect to login
437
Task.forResult(null)
438
}
439
}.onSuccess { protectedData ->
440
if (protectedData != null) {
441
displayUserData(protectedData)
442
} else {
443
showLoginScreen()
444
}
445
}
446
}
447
}
448
```
449
450
## Error Handling Patterns
451
452
Advanced error handling with tasks:
453
454
```kotlin
455
class TaskErrorHandling {
456
457
fun retryPattern() {
458
fun attemptOperation(attempt: Int = 1): Task<String> {
459
return Task.callInBackground {
460
if (Math.random() < 0.7 && attempt < 3) {
461
throw IOException("Network error (attempt $attempt)")
462
}
463
"Operation successful on attempt $attempt"
464
}.continueWithTask { task ->
465
if (task.isFaulted && attempt < 3) {
466
Log.w("Task", "Attempt $attempt failed, retrying...")
467
Task.delay(1000 * attempt).continueWithTask {
468
attemptOperation(attempt + 1)
469
}
470
} else {
471
Task.forResult(task.result ?: throw task.error!!)
472
}
473
}
474
}
475
476
attemptOperation().onSuccess { result ->
477
Log.d("Task", "Final result: $result")
478
}.continueWith { task ->
479
if (task.isFaulted) {
480
Log.e("Task", "All retry attempts failed: ${task.error?.message}")
481
}
482
null
483
}
484
}
485
486
fun fallbackPattern() {
487
Task.callInBackground {
488
fetchFromPrimarySource()
489
}.continueWithTask { task ->
490
if (task.isFaulted) {
491
Log.w("Task", "Primary source failed, trying fallback...")
492
Task.callInBackground { fetchFromFallbackSource() }
493
} else {
494
Task.forResult(task.result)
495
}
496
}.continueWithTask { task ->
497
if (task.isFaulted) {
498
Log.w("Task", "Fallback failed, using cache...")
499
Task.callInBackground { fetchFromCache() }
500
} else {
501
Task.forResult(task.result)
502
}
503
}.onSuccess { result ->
504
Log.d("Task", "Data retrieved: $result")
505
processData(result)
506
}.continueWith { task ->
507
if (task.isFaulted) {
508
Log.e("Task", "All sources failed: ${task.error?.message}")
509
showErrorMessage("Unable to load data")
510
}
511
null
512
}
513
}
514
515
fun timeoutPattern() {
516
val timeoutTask = Task.delay(5000).continueWith {
517
throw TimeoutException("Operation timed out")
518
}
519
520
val operationTask = Task.callInBackground {
521
performSlowOperation()
522
}
523
524
Task.whenAny(listOf(operationTask, timeoutTask))
525
.onSuccessTask { firstCompleted ->
526
if (firstCompleted == operationTask && !operationTask.isFaulted) {
527
Task.forResult(operationTask.result)
528
} else {
529
Task.forError<String>(TimeoutException("Operation timed out"))
530
}
531
}
532
.onSuccess { result ->
533
Log.d("Task", "Operation completed within timeout: $result")
534
}
535
.continueWith { task ->
536
if (task.isFaulted) {
537
Log.e("Task", "Operation failed or timed out: ${task.error?.message}")
538
}
539
null
540
}
541
}
542
}
543
```
544
545
## Integration with Facebook SDK
546
547
Using Bolts tasks with Facebook SDK operations:
548
549
```kotlin
550
class FacebookBoltsIntegration {
551
552
fun facebookLoginWithTasks(): Task<AccessToken> {
553
val tcs = TaskCompletionSource<AccessToken>()
554
555
val loginManager = LoginManager.getInstance()
556
loginManager.registerCallback(callbackManager, object : FacebookCallback<LoginResult> {
557
override fun onSuccess(result: LoginResult) {
558
tcs.setResult(result.accessToken)
559
}
560
561
override fun onCancel() {
562
tcs.setError(FacebookOperationCanceledException("Login cancelled"))
563
}
564
565
override fun onError(error: FacebookException) {
566
tcs.setError(error)
567
}
568
})
569
570
loginManager.logIn(activity, listOf("email", "public_profile"))
571
return tcs.task
572
}
573
574
fun graphRequestWithTasks(graphPath: String): Task<JSONObject> {
575
val tcs = TaskCompletionSource<JSONObject>()
576
577
val request = GraphRequest.newGraphPathRequest(
578
AccessToken.getCurrentAccessToken(),
579
graphPath
580
) { response ->
581
if (response.error != null) {
582
tcs.setError(FacebookGraphResponseException(response, response.error!!.errorMessage))
583
} else {
584
response.jsonObject?.let {
585
tcs.setResult(it)
586
} ?: tcs.setError(FacebookException("Empty response"))
587
}
588
}
589
590
request.executeAsync()
591
return tcs.task
592
}
593
594
fun completeUserSetupChain(): Task<Void> {
595
return facebookLoginWithTasks()
596
.onSuccessTask { accessToken ->
597
graphRequestWithTasks("/me?fields=id,name,email")
598
}
599
.onSuccessTask { userInfo ->
600
Task.callInBackground {
601
saveUserInfo(userInfo)
602
}
603
}
604
.onSuccessTask {
605
Task.callInBackground {
606
loadUserPreferences()
607
}
608
}
609
.onSuccess { preferences ->
610
setupUserSession(preferences)
611
null
612
}
613
}
614
}
615
```