0
0
Android Kotlinmobile~20 mins

CoroutineScope and dispatchers in Android Kotlin - Mini App: Build & Ship

Choose your learning style9 modes available
Build: Coroutine Dispatcher Demo
This screen demonstrates launching coroutines with different dispatchers to show how tasks run on various threads.
Target UI
-------------------------
| Coroutine Dispatcher Demo |
|-------------------------|
| [Start IO Task]          |
| [Start Default Task]     |
| [Start Main Task]        |
|                         |
| Logs:                   |
| >                       |
| >                       |
| >                       |
-------------------------
Add three buttons labeled 'Start IO Task', 'Start Default Task', and 'Start Main Task'.
When a button is tapped, launch a coroutine using the corresponding dispatcher: IO, Default, or Main.
Each coroutine should append a log message to a scrollable text area showing the dispatcher used and the thread name.
Ensure UI updates happen on the main thread.
Use CoroutineScope tied to the lifecycle of the screen.
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.fillMaxWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.Surface
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
      CoroutineDispatcherDemo()
    }
  }
}

@Composable
fun CoroutineDispatcherDemo() {
  // TODO: Add state for logs
  // TODO: Add CoroutineScope
  // TODO: Add UI with three buttons and a scrollable text area
  // TODO: Launch coroutines with appropriate dispatchers on button clicks
}
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.fillMaxWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.Surface
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
      CoroutineDispatcherDemo()
    }
  }
}

@Composable
fun CoroutineDispatcherDemo() {
  val logs = remember { mutableStateListOf<String>() }
  val scope = rememberCoroutineScope()
  val scrollState = rememberScrollState()

  fun logMessage(dispatcherName: String) {
    val threadName = Thread.currentThread().name
    logs.add("Running on $dispatcherName dispatcher, thread: $threadName")
  }

  Column(modifier = Modifier.fillMaxWidth()) {
    Button(onClick = {
      scope.launch(Dispatchers.IO) {
        logMessage("IO")
      }
    }, modifier = Modifier.fillMaxWidth()) {
      Text("Start IO Task")
    }
    Button(onClick = {
      scope.launch(Dispatchers.Default) {
        logMessage("Default")
      }
    }, modifier = Modifier.fillMaxWidth()) {
      Text("Start Default Task")
    }
    Button(onClick = {
      scope.launch(Dispatchers.Main) {
        logMessage("Main")
      }
    }, modifier = Modifier.fillMaxWidth()) {
      Text("Start Main Task")
    }
    Column(modifier = Modifier
      .weight(1f, fill = false)
      .verticalScroll(scrollState)) {
      for (log in logs) {
        Text(log)
      }
    }
  }
}

This solution uses rememberCoroutineScope() to get a scope tied to the composable lifecycle. Three buttons launch coroutines on different dispatchers: Dispatchers.IO for IO tasks, Dispatchers.Default for CPU-intensive tasks, and Dispatchers.Main for UI thread tasks.

Each coroutine appends a log message with the dispatcher name and current thread name to a mutableStateListOf, which updates the UI automatically. The logs are shown in a scrollable column so the user can see all messages.

This demonstrates how coroutines run on different threads depending on the dispatcher, and how to update UI safely from coroutines.

Final Result
Completed Screen
-------------------------
| Coroutine Dispatcher Demo |
|-------------------------|
| [Start IO Task]          |
| [Start Default Task]     |
| [Start Main Task]        |
|                         |
| Logs:                   |
| > Running on IO dispatcher, thread: DefaultDispatcher-worker-1 |
| > Running on Default dispatcher, thread: DefaultDispatcher-worker-2 |
| > Running on Main dispatcher, thread: main                    |
-------------------------
Tapping 'Start IO Task' launches a coroutine on IO dispatcher and logs the thread name.
Tapping 'Start Default Task' launches a coroutine on Default dispatcher and logs the thread name.
Tapping 'Start Main Task' launches a coroutine on Main dispatcher and logs the thread name.
Logs appear below buttons and update automatically.
Stretch Goal
Add a Clear Logs button to remove all log messages.
💡 Hint
Add a button that clears the mutableStateListOf logs when clicked.