Facebook SDK for Android providing comprehensive integration with Facebook platform features including Login, Sharing, Messenger, App Links, Analytics, and Graph API
—
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.
The core Task class for managing asynchronous operations:
class Task<TResult> private constructor() {
// Task state
val isCompleted: Boolean
val isCancelled: Boolean
val isFaulted: Boolean
val result: TResult?
val error: Exception?
// Continuation methods
fun <TContinuationResult> continueWith(
continuation: Continuation<TResult, TContinuationResult>
): Task<TContinuationResult>
fun <TContinuationResult> continueWith(
continuation: Continuation<TResult, TContinuationResult>,
executor: Executor
): Task<TContinuationResult>
fun <TContinuationResult> continueWithTask(
continuation: Continuation<TResult, Task<TContinuationResult>>
): Task<TContinuationResult>
fun <TContinuationResult> continueWithTask(
continuation: Continuation<TResult, Task<TContinuationResult>>,
executor: Executor
): Task<TContinuationResult>
// Success continuations
fun <TContinuationResult> onSuccess(
continuation: Continuation<TResult, TContinuationResult>
): Task<TContinuationResult>
fun <TContinuationResult> onSuccess(
continuation: Continuation<TResult, TContinuationResult>,
executor: Executor
): Task<TContinuationResult>
fun <TContinuationResult> onSuccessTask(
continuation: Continuation<TResult, Task<TContinuationResult>>
): Task<TContinuationResult>
fun <TContinuationResult> onSuccessTask(
continuation: Continuation<TResult, Task<TContinuationResult>>,
executor: Executor
): Task<TContinuationResult>
// Wait methods
fun waitForCompletion()
fun waitForCompletion(duration: Long, timeUnit: TimeUnit): Boolean
companion object {
// Task creation
fun <TResult> call(callable: Callable<TResult>): Task<TResult>
fun <TResult> call(callable: Callable<TResult>, executor: Executor): Task<TResult>
fun <TResult> callInBackground(callable: Callable<TResult>): Task<TResult>
fun <TResult> forResult(result: TResult): Task<TResult>
fun <TResult> forError(error: Exception): Task<TResult>
fun <TResult> cancelled(): Task<TResult>
// Task composition
fun <TResult> whenAll(tasks: Collection<Task<TResult>>): Task<List<TResult>>
fun whenAllResult(tasks: Collection<Task<*>>): Task<Void>
fun <TResult> whenAny(tasks: Collection<Task<TResult>>): Task<Task<TResult>>
fun whenAnyResult(tasks: Collection<Task<*>>): Task<Task<*>>
// Delay
fun delay(millis: Long): Task<Void>
fun delay(millis: Long, cancellationToken: CancellationToken): Task<Void>
}
}
interface Continuation<TTaskResult, TContinuationResult> {
@Throws(Exception::class)
fun then(task: Task<TTaskResult>): TContinuationResult
}class TaskExamples {
fun basicTaskUsage() {
// Create a task from a callable
val task = Task.call {
// Simulate some work
Thread.sleep(1000)
"Task completed successfully"
}
// Continue with the result
task.continueWith { task ->
if (task.isFaulted) {
Log.e("Task", "Task failed: ${task.error?.message}")
"Error occurred"
} else {
val result = task.result
Log.d("Task", "Task result: $result")
result?.uppercase()
}
}.continueWith { task ->
val finalResult = task.result
Log.d("Task", "Final result: $finalResult")
finalResult
}
}
fun backgroundTaskUsage() {
// Execute task in background
Task.callInBackground {
// Perform network request or database operation
fetchDataFromApi()
}.onSuccess { result ->
// This runs on the main thread by default
Log.d("Task", "Data fetched: $result")
updateUI(result)
}.continueWith { task ->
if (task.isFaulted) {
Log.e("Task", "Background task failed: ${task.error?.message}")
handleError(task.error)
}
null
}
}
fun chainedTasksExample() {
// Chain multiple asynchronous operations
Task.callInBackground {
authenticateUser()
}.onSuccessTask { authResult ->
// Return another task
Task.callInBackground {
fetchUserProfile(authResult.userId)
}
}.onSuccessTask { profile ->
// Another chained operation
Task.callInBackground {
loadUserPreferences(profile.id)
}
}.onSuccess { preferences ->
// Final success handler
Log.d("Task", "All operations completed")
setupUserSession(preferences)
}.continueWith { task ->
if (task.isFaulted) {
Log.e("Task", "Chain failed: ${task.error?.message}")
showLoginScreen()
}
null
}
}
}Manual control over task completion:
class TaskCompletionSource<TResult> {
val task: Task<TResult>
val isCancellationRequested: Boolean
fun setResult(result: TResult): Boolean
fun trySetResult(result: TResult): Boolean
fun setError(error: Exception): Boolean
fun trySetError(error: Exception): Boolean
fun setCancelled(): Boolean
fun trySetCancelled(): Boolean
}class ManualTaskControl {
fun createManualTask(): Task<String> {
val tcs = TaskCompletionSource<String>()
// Simulate async operation with manual completion
Thread {
try {
Thread.sleep(2000)
// Manually complete the task
tcs.setResult("Manual task completed")
} catch (e: InterruptedException) {
tcs.setError(e)
}
}.start()
return tcs.task
}
fun createConditionalTask(condition: Boolean): Task<String> {
val tcs = TaskCompletionSource<String>()
if (condition) {
tcs.setResult("Condition met")
} else {
tcs.setError(IllegalStateException("Condition not met"))
}
return tcs.task
}
fun createTimeoutTask(timeoutMs: Long): Task<String> {
val tcs = TaskCompletionSource<String>()
// Set up timeout
Handler(Looper.getMainLooper()).postDelayed({
if (!tcs.task.isCompleted) {
tcs.setError(TimeoutException("Task timed out"))
}
}, timeoutMs)
// Simulate work
Thread {
try {
Thread.sleep(timeoutMs + 1000) // Will timeout
tcs.trySetResult("Work completed")
} catch (e: Exception) {
tcs.trySetError(e)
}
}.start()
return tcs.task
}
}Handle task cancellation with cancellation tokens:
class CancellationToken {
val isCancellationRequested: Boolean
fun throwIfCancellationRequested()
fun register(action: Runnable): CancellationTokenRegistration
}
class CancellationTokenSource {
val token: CancellationToken
val isCancellationRequested: Boolean
fun cancel()
fun cancelAfter(delayMs: Long)
fun close()
}
class CancellationTokenRegistration {
fun close()
}class CancellationExamples {
fun cancellableTask() {
val cancellationSource = CancellationTokenSource()
val cancellationToken = cancellationSource.token
val task = Task.call({
// Long running operation
for (i in 1..100) {
// Check for cancellation
cancellationToken.throwIfCancellationRequested()
// Simulate work
Thread.sleep(100)
Log.d("Task", "Progress: $i/100")
}
"Task completed"
}, BoltsExecutors.background())
// Cancel after 5 seconds
Handler(Looper.getMainLooper()).postDelayed({
cancellationSource.cancel()
Log.d("Task", "Cancellation requested")
}, 5000)
task.continueWith { task ->
when {
task.isCancelled -> Log.d("Task", "Task was cancelled")
task.isFaulted -> Log.e("Task", "Task failed: ${task.error?.message}")
else -> Log.d("Task", "Task completed: ${task.result}")
}
null
}
}
fun cancellationWithCleanup() {
val cancellationSource = CancellationTokenSource()
// Register cleanup action
val registration = cancellationSource.token.register {
Log.d("Task", "Performing cleanup...")
cleanupResources()
}
val task = Task.callInBackground {
try {
performLongRunningOperation(cancellationSource.token)
} finally {
registration.close()
}
}
// Auto-cancel after timeout
cancellationSource.cancelAfter(10000) // 10 seconds
return task
}
}Control where tasks execute:
object BoltsExecutors {
fun background(): Executor
fun immediate(): Executor
}
object AndroidExecutors {
fun mainThread(): Executor
fun uiThread(): Executor
}class ExecutorExamples {
fun executorControlExample() {
// Background task
Task.call({
// Heavy computation
performCpuIntensiveWork()
}, BoltsExecutors.background())
.continueWith({ task ->
// Process result on background thread
processResult(task.result)
}, BoltsExecutors.background())
.continueWith({ task ->
// Update UI on main thread
updateUI(task.result)
null
}, AndroidExecutors.mainThread())
}
fun immediateExecutorExample() {
// Task that executes immediately on current thread
val result = Task.forResult("immediate result")
result.continueWith({ task ->
Log.d("Task", "This runs immediately: ${task.result}")
null
}, BoltsExecutors.immediate())
}
}Combine multiple tasks:
class TaskComposition {
fun parallelTasksExample() {
val task1 = Task.callInBackground { fetchUserData() }
val task2 = Task.callInBackground { fetchUserPreferences() }
val task3 = Task.callInBackground { fetchUserSettings() }
// Wait for all tasks to complete
Task.whenAll(listOf(task1, task2, task3))
.onSuccess { results ->
val userData = results[0]
val preferences = results[1]
val settings = results[2]
// Combine all results
setupUserSession(userData, preferences, settings)
}
.continueWith { task ->
if (task.isFaulted) {
Log.e("Task", "One or more tasks failed: ${task.error?.message}")
handleError(task.error)
}
null
}
}
fun raceConditionExample() {
val cacheTask = Task.callInBackground { loadFromCache() }
val networkTask = Task.callInBackground { loadFromNetwork() }
// Use whichever completes first
Task.whenAny(listOf(cacheTask, networkTask))
.onSuccess { firstCompletedTask ->
val winningTask = firstCompletedTask
if (!winningTask.isFaulted) {
Log.d("Task", "Using result from fastest source")
processData(winningTask.result)
} else {
Log.w("Task", "Fastest task failed, waiting for other...")
// Handle the case where first task failed
}
}
}
fun conditionalChainingExample() {
Task.callInBackground {
checkUserAuthentication()
}.onSuccessTask { isAuthenticated ->
if (isAuthenticated) {
// User is authenticated, fetch protected data
Task.callInBackground { fetchProtectedUserData() }
} else {
// User not authenticated, redirect to login
Task.forResult(null)
}
}.onSuccess { protectedData ->
if (protectedData != null) {
displayUserData(protectedData)
} else {
showLoginScreen()
}
}
}
}Advanced error handling with tasks:
class TaskErrorHandling {
fun retryPattern() {
fun attemptOperation(attempt: Int = 1): Task<String> {
return Task.callInBackground {
if (Math.random() < 0.7 && attempt < 3) {
throw IOException("Network error (attempt $attempt)")
}
"Operation successful on attempt $attempt"
}.continueWithTask { task ->
if (task.isFaulted && attempt < 3) {
Log.w("Task", "Attempt $attempt failed, retrying...")
Task.delay(1000 * attempt).continueWithTask {
attemptOperation(attempt + 1)
}
} else {
Task.forResult(task.result ?: throw task.error!!)
}
}
}
attemptOperation().onSuccess { result ->
Log.d("Task", "Final result: $result")
}.continueWith { task ->
if (task.isFaulted) {
Log.e("Task", "All retry attempts failed: ${task.error?.message}")
}
null
}
}
fun fallbackPattern() {
Task.callInBackground {
fetchFromPrimarySource()
}.continueWithTask { task ->
if (task.isFaulted) {
Log.w("Task", "Primary source failed, trying fallback...")
Task.callInBackground { fetchFromFallbackSource() }
} else {
Task.forResult(task.result)
}
}.continueWithTask { task ->
if (task.isFaulted) {
Log.w("Task", "Fallback failed, using cache...")
Task.callInBackground { fetchFromCache() }
} else {
Task.forResult(task.result)
}
}.onSuccess { result ->
Log.d("Task", "Data retrieved: $result")
processData(result)
}.continueWith { task ->
if (task.isFaulted) {
Log.e("Task", "All sources failed: ${task.error?.message}")
showErrorMessage("Unable to load data")
}
null
}
}
fun timeoutPattern() {
val timeoutTask = Task.delay(5000).continueWith {
throw TimeoutException("Operation timed out")
}
val operationTask = Task.callInBackground {
performSlowOperation()
}
Task.whenAny(listOf(operationTask, timeoutTask))
.onSuccessTask { firstCompleted ->
if (firstCompleted == operationTask && !operationTask.isFaulted) {
Task.forResult(operationTask.result)
} else {
Task.forError<String>(TimeoutException("Operation timed out"))
}
}
.onSuccess { result ->
Log.d("Task", "Operation completed within timeout: $result")
}
.continueWith { task ->
if (task.isFaulted) {
Log.e("Task", "Operation failed or timed out: ${task.error?.message}")
}
null
}
}
}Using Bolts tasks with Facebook SDK operations:
class FacebookBoltsIntegration {
fun facebookLoginWithTasks(): Task<AccessToken> {
val tcs = TaskCompletionSource<AccessToken>()
val loginManager = LoginManager.getInstance()
loginManager.registerCallback(callbackManager, object : FacebookCallback<LoginResult> {
override fun onSuccess(result: LoginResult) {
tcs.setResult(result.accessToken)
}
override fun onCancel() {
tcs.setError(FacebookOperationCanceledException("Login cancelled"))
}
override fun onError(error: FacebookException) {
tcs.setError(error)
}
})
loginManager.logIn(activity, listOf("email", "public_profile"))
return tcs.task
}
fun graphRequestWithTasks(graphPath: String): Task<JSONObject> {
val tcs = TaskCompletionSource<JSONObject>()
val request = GraphRequest.newGraphPathRequest(
AccessToken.getCurrentAccessToken(),
graphPath
) { response ->
if (response.error != null) {
tcs.setError(FacebookGraphResponseException(response, response.error!!.errorMessage))
} else {
response.jsonObject?.let {
tcs.setResult(it)
} ?: tcs.setError(FacebookException("Empty response"))
}
}
request.executeAsync()
return tcs.task
}
fun completeUserSetupChain(): Task<Void> {
return facebookLoginWithTasks()
.onSuccessTask { accessToken ->
graphRequestWithTasks("/me?fields=id,name,email")
}
.onSuccessTask { userInfo ->
Task.callInBackground {
saveUserInfo(userInfo)
}
}
.onSuccessTask {
Task.callInBackground {
loadUserPreferences()
}
}
.onSuccess { preferences ->
setupUserSession(preferences)
null
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-com-facebook-android--facebook-android-sdk