Week 8
Coroutines + Wallpaper App Setup continues
Goals:
1. HW Discussion + Wallpaper App Setup
2. Coroutines Introdocution - Async
3. Homework for Weather App (Interview Style)
Story time:
https://www.youtube.com/watch?v=YsMYSRw3BPU

Tutorial Hell
What is Async programming?

2 people wanting to dringk coffee but only one can have it without straw
in other words

1. don't stop my app when one process is running
2. don't return result immediately but later
e.g. network requests, saving in db
Concurrent Programming
Async Programming ->
Parallel Processing
Use your CPU in a way that you can achieve more than one process at one point of time
3 Types ->
1. UI / Main Thread : UI Stuff
setting wallpaper / toast / add a new button
Dispatchers.Default
2. I/O -> Input Output Process
Retrofit (DOWNLOADING, UPLODAING DATA)
Dispatchers.IO
3. Default
Dispatcher.main
not sure if you should optimize
Previous implementations
Call<User> call = apiService.getUser(1);
call.enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
if (response.isSuccessful()) {
User user = response.body();
// Process the user data
} else {
// Handle error
}
}
@Override
public void onFailure(Call<User> call, Throwable t) {
// Handle network failure
}
});
Example with 2 req
// First network call
Call<User> call1 = apiService.getUser(1);
call1.enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
if (response.isSuccessful()) {
User user = response.body();
// Process the first user data
// Second network call based on the result of the first call
Call<SomeOtherData> call2 = apiService.getSomeOtherData(user.getId());
call2.enqueue(new Callback<SomeOtherData>() {
@Override
public void onResponse(Call<SomeOtherData> call, Response<SomeOtherData> response) {
if (response.isSuccessful()) {
SomeOtherData otherData = response.body();
// Process the second data
} else {
// Handle error for the second call
}
}
@Override
public void onFailure(Call<SomeOtherData> call, Throwable t) {
// Handle network failure for the second call
}
});
} else {
// Handle error for the first call
}
}
@Override
public void onFailure(Call<User> call, Throwable t) {
// Handle network failure for the first call
}
});
Problems with this approach
1. Complicated and hard to read
2. you have to handle in clear to clear in garbage collector
fun onCleared() { // in viewmodel
call?.cancel()
}
RxJava Implementation
apiService.getUser(1)
.flatMap(user -> apiService.getSomeOtherData(user.getId()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
someOtherData -> {
// Process the second data
},
throwable -> {
// Handle network failure for the second call
}
);
Now Corooutine Implementation
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
val apiService = retrofit.create(ApiService::class.java)
viewModelScope.launch(Dispatchers.IO) { // IO thread if you don't pass then it's main thread
try {
val user = apiService.getUser(1)
// Process the user data
val someOtherData = apiService.getSomeOtherData(user.id)
// Process the second data
} catch (e: Exception) {
// Handle network failure for either call
}
}
// suspend is non blocking but delays the result only
Thread.sleep vs delay (thread below)
package com.example.wallpaperempty.data.api.model
fun main() {
val mainStartTime = System.currentTimeMillis()
println("Starting the task... (Time: $mainStartTime ms)")
val startTime = System.currentTimeMillis()
// Simulate a background task with Thread.sleep
Thread {
Thread.sleep(3000) // Sleep for 3 seconds
val endTime = System.currentTimeMillis()
val elapsedTime = endTime - startTime
println("Task completed after 3 seconds. Elapsed time: $elapsedTime milliseconds")
}.start()
println("Task initiated, but main thread is blocked...")
val mainEndTime = System.currentTimeMillis()
val mainElapsedTime = mainEndTime - mainStartTime
val mainThreadResumeStartTime = System.currentTimeMillis()
// Main thread is blocked for 5 seconds
Thread.sleep(5000) // Sleep for 5 seconds
val mainThreadResumeEndTime = System.currentTimeMillis()
val mainThreadResumeElapsedTime = mainThreadResumeEndTime - mainThreadResumeStartTime
println("Main thread resumed. Elapsed time in the main thread: $mainElapsedTime milliseconds")
println("Main thread resume elapsed time: $mainThreadResumeElapsedTime milliseconds")
}
Thread.sleep vs delay (delay below)
import kotlinx.coroutines.*
// Function to simulate a background task with delay
suspend fun simulateBackgroundTask() {
val startTime = System.currentTimeMillis()
delay(3000) // Delay for 3 seconds (3000 milliseconds)
val endTime = System.currentTimeMillis()
val elapsedTime = endTime - startTime
println("Task completed after 3 seconds. Elapsed time: $elapsedTime milliseconds")
}
fun main() = runBlocking {
val mainStartTime = System.currentTimeMillis()
println("Starting the task... (Time: $mainStartTime ms)")
// Launch a coroutine to perform the background task
launch {
simulateBackgroundTask()
}
println("Task initiated, but not blocked...${System.currentTimeMillis()}")
val backgroundTaskStartTime = System.currentTimeMillis()
// Give some time for the background task to complete
delay(5000) // Delay for 5 seconds (5000 milliseconds)
val backgroundTaskEndTime = System.currentTimeMillis()
val backgroundTaskElapsedTime = backgroundTaskEndTime - backgroundTaskStartTime
val mainEndTime = System.currentTimeMillis()
val mainElapsedTime = mainEndTime - mainStartTime
println("Main thread resumed. Elapsed time in the main thread: $mainElapsedTime milliseconds")
println("Background task elapsed time: $backgroundTaskElapsedTime milliseconds")
}
Result: Lukas Lechner

Suspend functions
suspend fun performTask(): String {
delay(2000) // Simulate a 2-second delay (non-blocking)
return "Task Completed"
}
fun main() {
println("Starting the task...")
val job = GlobalScope.launch {
val result = performTask()
println(result)
}
// While the background task is running, the main thread can do other work
println("Main thread is not blocked...")
}
Delay(3000) -> print() -> not printing
print("Hello")
print("After dely not blocking")
3 Types of Coroutine Builders
Coroutine Builder | Purpose | Result Type | Use Case | Example |
---|---|---|---|---|
launch | Start a background coroutine without waiting for it | Job | Fire-and-forget tasks, parallel execution | val job = GlobalScope.launch { // Do some background work } |
async | Start a background coroutine and await a result | Deferred<T> | Concurrent tasks with results, parallelism | val deferred = GlobalScope.async { // Do some background work and return a result "Result" } val result = deferred.await() |
runBlocking | Block the main thread temporarily | N/A | Main functions, testing, avoid in main thread |
runBlocking { // Code that runs in a blocking manner } |
Global scope. launch
val job = GlobalScope.launch {
delay(500)
println("printed in coroouint")
// Network Call #1
//user = getUser()
// Network Call #2
// getamazonorders(user)
}
println("main ends")
// nothing is printed unless thread.sleep
Why?
runBlocking
fun main() {
println("Start")
runBlocking {
println("Before delay.")
delay(1000)
println("After delay.")
}
println("End")
}
async
fun main() {
log("Start")
runBlocking {
log("Start of runBlocking")
val number = async {
log("Before delay.")
delay(1000)
log("After delay.")
(1..100).random()
}
log("Continue with runBlocking...")
log("End of runBlocking with value ${number.await()}")
}
log("End")
}
RealWorld Example
package com.example.wallpaperempty.presentation.adapter
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
fun simpleFlow(): Flow<Int> = flow {
for (i in 1..5) {
delay(1000) // Simulate some asynchronous operation
emit(i) // Emit values one by one
}
}
fun main() {
test()
Thread.sleep(6000)
}
fun test() {
GlobalScope.launch {
val flow = simpleFlow()
println("Start collecting flow")
flow.collect { value ->
println("Received: $value")
}
println("Flow collection completed")
}
}
Rules
1. Suspend Function Should not block the caller thread
Documentation
https://medium.com/kotlin-en-android/coroutines-con-kotlin-introducci%C3%B3n-a68f5eeee6a8
https://developer.android.com/kotlin/coroutines
Homework
1. Weather App: with API key moved out
2. Build UI for the App while handling weather of a location
3. Be creative : Store images or use a Text to Image API
Week 8 why we need coroutines?
By Harnoor Singh
Week 8 why we need coroutines?
- 245