64.1 Creating the Coroutine Example Application
Select the Create New Project quick start option from the welcome screen and, within the resulting new project dialog, choose the Empty Activity template before clicking on the Next button.
Enter CoroutineDemo into the Name field and specify com.ebookfrenzy.coroutinedemo as the package name. Before clicking on the Finish button, change the Minimum API level setting to API 26: Android 8.0 (Oreo) and the Language menu to Kotlin. Migrate the project to view binding using the steps outlined in section “Migrating a Project to View Binding”.
64.2 Adding Coroutine Support to the Project
The current version of Android Studio does not automatically include support for coroutines in newly created projects. Before proceeding, therefore, edit the Gradle Scripts -> build.gradle (Module: CoroutineDemo.app) file and add the following lines to the dependencies section (noting, as always, that newer versions of the libraries may be available):
dependencies {
.
.
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
.
.
}
After making the change, click on the Sync Now link at the top of the editor panel to commit the changes.
64.3 Designing the User Interface
The user interface will consist of a button to launch coroutines together with a Seekbar to specify how many coroutines are to be launched asynchronously each time the button is clicked. As the coroutines execute, a TextView will update when individual coroutines start and end.
Begin by loading the activity_main.xml layout file and add the Button, TextView and SeekBar objects so that the layout resembles that shown in Figure 64-1:
To implement the layout constraints shown above begin by clearing all constraints on the layout using the toolbar button. Shift-click on the four objects so that all are selected, right-click over the top-most TextView and select the Center -> Horizontally menu option. Right-click once again, this time selecting the Chains -> Create Vertical Chain option.
Select the SeekBar and change the layout_width property to 0dp (match_constraints) before adding a 24dp margin on the left and right hand...
The SeekBar controls the number of asynchronous coroutines, ranging from 1 to 2000, that are launched each time the button is clicked. Remaining within the activity_main.xml file, select the SeekBar and use the Attributes tool window to change the max property to 2000. Next, edit the MainActivity.kt file, add a variable in which to store the current slider setting and modify the onCreate() method to add a SeekBar listener:
.
.
import android.widget.SeekBar
.
.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private var count: Int = 1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
&...
64.5 Adding the Suspend Function
When the user taps the button the app will need to launch the number of coroutines selected in the SeekBar. The launchCoroutines() onClick method will achieve this using the coroutine launch builder to execute a suspend function. Since the suspend function will return a status string to be displayed on the statusText TextView object, it will need to be implemented using the async builder. All of these actions will need to be performed within a coroutine scope which also needs to be declared. Within the MainActivity.kt file make the following changes:
.
.
import kotlinx.coroutines.*
.
.
class MainActivity : AppCompatActivity() {
private val coroutineScope = CoroutineScope(Dispatchers.Main)
.
.
suspend fun performTask(tasknumber: Int): Deferred<String> =
coroutineScope.async(Dispatchers.Main) {
...
64.6 Implementing the launchCoroutines Method
The final task before testing the app is to add the launchCoroutines() method which is called when the Button object is clicked. This method should be added to the MainActivity.kt file as follows:
.
.
import android.view.View
.
.
fun launchCoroutines(view: View) {
(1..count).forEach {
binding.statusText.text = "Started Coroutine ${it}"
coroutineScope.launch(Dispatchers.Main) {
binding.statusText.text = performTask(it).await()
}
}
...
Build and run the app on a device or emulator and move the SeekBar to a low number (for example 10) before tapping the launch button. The status text will update each time a coroutine is launched until the maximum is reached. After each coroutine completes the 5 second delay the status text will update until all 10 have completed (in practice these status updates will happen so quickly it will be difficult to see the status changes).
Repeat the process with the SeekBar set to 2000, this time sliding the seekbar back and forth as the coroutines run to verify that the main thread is still running and has not been blocked.
Finally, with the Logcat panel displayed, set the SeekBar to 2000 and click on the launch button repeatedly. After about 15 clicks the Logcat panel will begin displaying messages similar to the following:
I/Choreographer: Skipped 52 frames! The application may be doing too much work on its main thread.
Although the app continues to...
Building on the information covered in “An Introduction to Kotlin Coroutines”, this chapter has stepped through the creation of an example app that demonstrates the use of Kotlin coroutines within an Android app. The example demonstrated the use of the Main dispatcher to launch thousands of asynchronous coroutines, including returning results.