Bird
Raised Fist0
Android Kotlinmobile~20 mins

launch and async builders in Android Kotlin - Mini App: Build & Ship

Choose your learning style10 modes available
Build: Async Data Loader
This screen loads data asynchronously and shows loading status. It uses Kotlin coroutines with launch and async builders to fetch data in the background and update the UI when done.
Target UI
-------------------------
| Async Data Loader      |
|-----------------------|
| [Load Data]           |
|                       |
| Status: Idle          |
|                       |
| Data:                 |
|                       |
|                       |
-------------------------
Add a button labeled 'Load Data' that starts loading data asynchronously when tapped.
Use a coroutine launch builder to start the loading process.
Inside launch, use async builder to simulate fetching data with a delay.
Show 'Loading...' in the status text while data is loading.
When data is loaded, update the status to 'Data loaded' and display the fetched data below.
Handle coroutine scope properly to avoid leaks.
Starter Code
Android Kotlin
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.*

class MainActivity : ComponentActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
      MaterialTheme {
        Surface(modifier = Modifier.padding(16.dp)) {
          AsyncDataLoaderScreen()
        }
      }
    }
  }
}

@Composable
fun AsyncDataLoaderScreen() {
  var status by remember { mutableStateOf("Idle") }
  var data by remember { mutableStateOf("") }

  // TODO: Add coroutine scope and implement button click to load data asynchronously

  Column {
    Button(onClick = { /* TODO: Start loading data */ }) {
      Text("Load Data")
    }
    Text("Status: $status", modifier = Modifier.padding(top = 16.dp))
    Text("Data: $data", modifier = Modifier.padding(top = 8.dp))
  }
}
Task 1
Task 2
Task 3
Task 4
Task 5
Solution
Android Kotlin
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.*

class MainActivity : ComponentActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
      MaterialTheme {
        Surface(modifier = Modifier.padding(16.dp)) {
          AsyncDataLoaderScreen()
        }
      }
    }
  }
}

@Composable
fun AsyncDataLoaderScreen() {
  var status by remember { mutableStateOf("Idle") }
  var data by remember { mutableStateOf("") }
  val scope = rememberCoroutineScope()

  Column {
    Button(onClick = {
      scope.launch {
        status = "Loading..."
        val deferredData = async {
          delay(2000) // Simulate network or heavy work
          "Here is your loaded data!"
        }
        data = deferredData.await()
        status = "Data loaded"
      }
    }) {
      Text("Load Data")
    }
    Text("Status: $status", modifier = Modifier.padding(top = 16.dp))
    Text("Data: $data", modifier = Modifier.padding(top = 8.dp))
  }
}

We use rememberCoroutineScope() to get a coroutine scope tied to the composable lifecycle. When the user taps the 'Load Data' button, we launch a coroutine in this scope.

Inside the coroutine, we first update the status to 'Loading...'. Then we use async builder to simulate fetching data with a 2-second delay. This runs concurrently and returns a deferred result.

We await() the result from async, then update the data state and set status to 'Data loaded'. This updates the UI automatically.

This approach keeps the UI responsive and shows loading feedback while data is fetched asynchronously.

Final Result
Completed Screen
-------------------------
| Async Data Loader      |
|-----------------------|
| [Load Data]           |
|                       |
| Status: Data loaded   |
|                       |
| Data:                 |
| Here is your loaded   |
| data!                 |
-------------------------
User taps 'Load Data' button.
Status text changes to 'Loading...'.
After 2 seconds, status changes to 'Data loaded'.
Fetched data text appears below status.
Stretch Goal
Add a cancel button to stop loading if user changes their mind.
💡 Hint
Use a Job reference to cancel the coroutine launched in the scope.

Practice

(1/5)
1. What is the main difference between launch and async builders in Kotlin coroutines?
easy
A. launch is used only for UI updates, async for background tasks.
B. launch blocks the main thread, async does not.
C. launch returns a value immediately, async never returns.
D. launch does not return a result, async returns a Deferred result.

Solution

  1. Step 1: Understand launch behavior

    launch starts a coroutine that runs code asynchronously but does not return a value.
  2. Step 2: Understand async behavior

    async starts a coroutine and returns a Deferred object, which you can await to get a result.
  3. Final Answer:

    launch does not return a result, async returns a Deferred result. -> Option D
  4. Quick Check:

    launch = no result, async = returns result [OK]
Hint: Remember: async returns a result, launch does not [OK]
Common Mistakes:
  • Thinking launch returns a value
  • Confusing async with blocking calls
  • Assuming launch blocks the main thread
2. Which of the following is the correct syntax to start a coroutine with async in Kotlin?
easy
A. async.launch { fetchData() }
B. val result = launch { fetchData() }
C. val result = async { fetchData() }
D. launch.async { fetchData() }

Solution

  1. Step 1: Identify correct coroutine builder usage

    async is called as a function returning a Deferred, so val result = async { fetchData() } is correct.
  2. Step 2: Check other options for syntax errors

    launch does not return a value, so val result = launch { fetchData() } is wrong for async usage. Options C and D have invalid syntax.
  3. Final Answer:

    val result = async { fetchData() } -> Option C
  4. Quick Check:

    async returns Deferred, so assign it [OK]
Hint: async returns Deferred, assign it to a variable [OK]
Common Mistakes:
  • Using launch instead of async when expecting a result
  • Trying to chain async.launch or launch.async
  • Not assigning async result to a variable
3. What will be the output of this Kotlin coroutine code snippet?
fun main() = runBlocking {
  val deferred = async {
    delay(100)
    10 + 20
  }
  println("Result: ${deferred.await()}")
}
medium
A. Result: 0
B. Result: 30
C. Compilation error
D. Runtime exception

Solution

  1. Step 1: Understand async and await behavior

    The async block runs asynchronously and returns a Deferred. Calling await() waits for the result.
  2. Step 2: Calculate the expression inside async

    The code delays 100ms then returns 10 + 20 = 30, which is printed.
  3. Final Answer:

    Result: 30 -> Option B
  4. Quick Check:

    async + await returns computed value [OK]
Hint: await() gets async result after delay [OK]
Common Mistakes:
  • Forgetting to call await() to get result
  • Expecting immediate result without delay
  • Confusing launch with async return
4. Identify the error in this Kotlin coroutine code:
fun main() = runBlocking {
  val result = launch {
    val data = fetchData()
  }
  println(data)
}

suspend fun fetchData(): String {
  delay(100)
  return "Hello"
}
medium
A. Variable 'data' is not accessible outside launch block
B. fetchData() cannot be called inside launch
C. runBlocking cannot be used with launch
D. Missing await() on launch

Solution

  1. Step 1: Check variable scope of 'data'

    The variable 'data' is declared inside the launch coroutine block, so it is not visible outside.
  2. Step 2: Analyze println(data) usage

    Trying to print 'data' outside launch causes a compilation error because 'data' is undefined there.
  3. Final Answer:

    Variable 'data' is not accessible outside launch block -> Option A
  4. Quick Check:

    Variables inside launch are local to its block [OK]
Hint: Variables inside launch are local; can't access outside [OK]
Common Mistakes:
  • Assuming launch returns a value
  • Trying to await launch (which returns Job)
  • Ignoring variable scope rules
5. You want to fetch two independent data sources concurrently and combine their results. Which Kotlin coroutine approach is best?
val data1 = async { fetchData1() }
val data2 = async { fetchData2() }
val combined = data1.await() + data2.await()
Why is this approach preferred over launching two launch coroutines and combining results later?
hard
A. async returns Deferred results that can be awaited and combined immediately
B. launch blocks the main thread, async does not
C. launch cannot run multiple coroutines concurrently
D. async automatically caches results, launch does not

Solution

  1. Step 1: Understand concurrency with async

    Using async starts coroutines that return Deferred results, allowing you to wait for and combine their outputs.
  2. Step 2: Compare with launch behavior

    launch returns Job and does not provide a direct result, so combining outputs requires extra state management.
  3. Final Answer:

    async returns Deferred results that can be awaited and combined immediately -> Option A
  4. Quick Check:

    async = awaitable result, launch = no result [OK]
Hint: Use async to get results you can combine easily [OK]
Common Mistakes:
  • Thinking launch returns a value
  • Assuming launch blocks main thread
  • Believing async caches results automatically
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard