Coroutines

Coroutines background processing

Pinterest LinkedIn Tumblr

In this blog, We’ll learn coroutines background processing by creating a small project, that simply displays an image to the UI.

Objective

  • Get Image from URL – So first we’re going to get an image from URL and obviously we can not do that on the main thread. So we need a background thread to do that. And we’re going to this with Coroutines.
  • Apply Processing – Next stuff is after downloading the image. We’re going to use another dispatcher to process that image. So here we’re going to apply a filter to that image. So that we transform it in a certain way. We’re going to convert it to black and white. We will do that in a separate coroutine for background processing.
  • Show it on ImageView – Finally, we’re going to show it on ImageView.

So it quite a simple idea, But the way we do it nicely. Because we are going to use coroutine for background processing. So while we are working live project we can understand how we can perform background processing with the bits of help of Coroutines.

Requirements

Importantly I want to mention here that there are some requirements for this example.

  • Android Studio – First of all you have to AndroidStudio installed in your machine.
  • Android Knowledge – You would have some Android development knowledge.

All right just go ahead and jump into the Android Studio.

Let’s create a new project with basic template. So first of all, I’m going to create a basic interface and you not have to worry about it, you can download the source code from bottom of this blog.

Add permission

Obviously, first thing we are going to download an image from server so we have to require Internet permission. So let open the android manifest add it.

    <uses-permission android:name="android.permission.INTERNET" />

I’m going to use this URL here, So this is what we will downloading and this is what we will processing.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="500dp"
        android:layout_height="500dp"
        android:scaleType="fitCenter"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:visibility="gone"
        app:srcCompat="@mipmap/ic_launcher" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Alright moving on we have a layout that has basically two elements. It has ProgressBar that will just show that something is going on in IO dispatcher. and it has an ImageView that is currently hidden. So this will become visible when we actually have something to display. and then we’ll hide the progressbar.

MainActivity

Create Filter

package com.backgroundprocessingexample

import android.graphics.Bitmap
import android.graphics.Color

object Filter {

    fun apply(source: Bitmap): Bitmap {
        val width = source.width
        val height = source.height
        val pixels = IntArray(width * height)
        // get pixel array from source
        source.getPixels(pixels, 0, width, 0, 0, width, height)

        var R: Int
        var G: Int
        var B: Int
        var index: Int
        var threshHold: Int

        for (y in 0 until height) {
            for (x in 0 until width) {
                // get current index in 2D-matrix
                index = y * width + x
                // get color
                R = Color.red(pixels[index])
                G = Color.green(pixels[index])
                B = Color.blue(pixels[index])
                val grey = (R + G + B) / 3
                pixels[index] = Color.rgb(grey, grey, grey)
            }
        }

        // output bitmap
        val bitmapOut = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
        bitmapOut.setPixels(pixels, 0, width, 0, 0, width, height)
        return bitmapOut
    }
}

one things that I added here it is the Filter. Now this filter has a function fun apply(source: Bitmap) on a bitmap and it’s simply converted to a bitmap and It’s simply converted to a bitmap out to a another image.

What this filter does, It converts images to black & white. It does handle some processing because it goes pixel by pixel to convert it into black & white. That will take some processing power. So that a background job, So we can apply our coroutines to it. We can’t do it on the main thread or It should not do this in main thread.

    // Add Coroutines Dependency
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'

So here in the dependency first of all go ahead and copy the above dependencies. After adding these dependencies let go ahead and sync the project.

Now let’s open the MainActivity, So first i want to load image here from URL to local bitmap. then we want to put that bitmap in our ImageView. So I’m creating a kotlin function.

    private fun getImageFromUrl(): Bitmap =
            URL(IMAGE_URL).openStream().use {
                BitmapFactory.decodeStream(it)
            }

    private fun loadImage(bmp: Bitmap) {
        progressBar.visibility = View.GONE
        imageView.setImageBitmap(bmp)
        imageView.visibility = View.VISIBLE
    }

So this function simply returns a bitmap of whatever is returned from decode stream. Now Important thing is this is a network call. So we can not do that in main thread. So what we’ll like to do is we would like to open up a coroutines scope and do that call inside the scope on the IO dispatcher.

 coroutineScope.launch {
            val remoteImageDeferred = coroutineScope.async(Dispatchers.IO) {
                getImageFromUrl()
            }
            val imageBitmap = remoteImageDeferred.await()
            //loadImage(imageBitmap)
            launch(Dispatchers.Default) {
             val filterBitmap =   Filter.apply(imageBitmap)
                withContext(Dispatchers.Main) {
                    loadImage(filterBitmap)
                }

               // Log.i(MainActivity.TAG, "Default. Thread: ${Thread.currentThread().name}")
            }
        }

So let create a coroutines scope, here I’m going to open the main scope. Because we have to update UI from IO. Otherwise, we can’t update UI from another thread, It has to from main thread.

So the final output is

package com.backgroundprocessingexample

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.*
import java.net.URL

class MainActivity : AppCompatActivity() {

    private val IMAGE_URL = "https://androidwave.com/wp-content/uploads/2018/12/useful-tools-for-logging-debugging-in-android.png.webp"
    private val coroutineScope = CoroutineScope(Dispatchers.Main)


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        coroutineScope.launch {
            val remoteImageDeferred = coroutineScope.async(Dispatchers.IO) {
                getImageFromUrl()
            }
            val imageBitmap = remoteImageDeferred.await()
            //loadImage(imageBitmap)
            launch(Dispatchers.Default) {
             val filterBitmap =   Filter.apply(imageBitmap)
                withContext(Dispatchers.Main) {
                    loadImage(filterBitmap)
                }

               // Log.i(MainActivity.TAG, "Default. Thread: ${Thread.currentThread().name}")
            }
        }

    }

    private fun getImageFromUrl(): Bitmap =
            URL(IMAGE_URL).openStream().use {
                BitmapFactory.decodeStream(it)
            }

    private fun loadImage(bmp: Bitmap) {
        progressBar.visibility = View.GONE
        imageView.setImageBitmap(bmp)
        imageView.visibility = View.VISIBLE
    }
}

Conclusion

That all, this way you can perform background jobs using coroutines in Kotlin. Keep in touch for more coroutines tutorials

What is recommendation

If you are a beginner I would like to recommend please read basic concept of coroutines.

1 Comment

  1. This code has error progressbar and imageview has not been initialized, please change the code, Thanks

Write A Comment