안드로이드 - 코루틴으로 URL 이미지 불러오기

By JS | Last updated: May 13, 2022

안드로이드의 코루틴으로 웹에 있는, URL 이미지를 다운로드하여 화면에 보여주는 방법을 소개합니다.

1. Coroutine 의존성 설정

Gradle 프로젝트에서 코루틴을 사용하려면 아래와 같이 build.gradle에 의존성을 추가해야 합니다.

dependencies {
    ...
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
}

2. View(activity_main.xml) 구현

Activity에는 아래와 같이 이미지를 보여줄 ImageView와 ProgressBar를 추가하였습니다.

<?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>

3. 코루틴으로 URL 이미지 다운로드 및 View에 보여주기

코루틴에서 URL 이미지를 다운로드하고, 다운로드가 완료되면 이미지를 View에 보여주도록 구현해야 합니다. 아래 예제에서는 URL 이미지를 다운로드할 때는 Dispatchers.IO를 사용하며 async로 가져옵니다. async는 Deffered를 리턴하며 Deferred.await()처럼 await()을 호출하면 비동기적인 작업이 완료될 때까지 대기하게 됩니다. 작업이 완료되면 결과가 리턴됩니다. getOriginalBitmap()에서 Bitmap이 리턴되면 Main thread(Dispatchers.Main)에서 loadImage()를 호출하여 이미지를 View에 보여줍니다.

package com.example.coroutineexample

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.ImageView
import android.widget.ProgressBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import java.net.URL

class MainActivity : AppCompatActivity() {

    private val IMAGE_URL = "https://raw.githubusercontent.com/DevTides/JetpackDogsApp/master/app/src/main/res/drawable/dog.png"
    private val coroutineScope = CoroutineScope(Dispatchers.Main)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        coroutineScope.launch {
            val originalDeferred = coroutineScope.async(Dispatchers.IO) {
                getOriginalBitmap()
            }

            val originalBitmap = originalDeferred.await()
            loadImage(originalBitmap)
        }
    }

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

    private fun loadImage(bmp: Bitmap) {
        val progressBar = findViewById<ProgressBar>(R.id.progressBar)
        val imageView = findViewById<ImageView>(R.id.imageView)
        progressBar.visibility = View.GONE
        imageView.setImageBitmap(bmp)
        imageView.visibility = View.VISIBLE
    }
}

위 코드를 실행하면 다음과 같이 이미지가 보입니다. coroutine url image loading

4. 이미지 처리

위의 예제는 async()로 이미지 다운로드를 하고 화면에 보여줍니다. 만약 이미지를 다운로드한 뒤에 약간의 가공을 하고 그 이미지를 화면에 보여주려면 async()를 추가하여 이미지 처리를 하도록 구현할 수 있습니다.

아래 예제는 Dispatchers.IO에서 async로 이미지를 다운로드 받고, Dispatchers.Default에서 async로 이미지 처리를 수행합니다. 결과가 리턴되면 Dispatchers.Main에서 Image를 View에 보여주도록 합니다.

package com.example.coroutineexample

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Bundle
import android.view.View
import android.widget.ImageView
import android.widget.ProgressBar
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import java.net.URL

class MainActivity2 : AppCompatActivity() {

    private val IMAGE_URL = "https://raw.githubusercontent.com/DevTides/JetpackDogsApp/master/app/src/main/res/drawable/dog.png"
    private val coroutineScope = CoroutineScope(Dispatchers.Main)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        coroutineScope.launch {
            val originalDeferred = coroutineScope.async(Dispatchers.IO) {
                getOriginalBitmap()
            }

            val originalBitmap = originalDeferred.await()
            val filteredDeferred =  coroutineScope.async(Dispatchers.Default) {
                applyFilter(originalBitmap)
            }

            val filterBitmap = filteredDeferred.await()
            loadImage(filterBitmap)
        }
    }

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

    private fun applyFilter(bmp: Bitmap): Bitmap =
        Filter.apply(bmp)

    private fun loadImage(bmp: Bitmap) {
        val progressBar = findViewById<ProgressBar>(R.id.progressBar)
        val imageView = findViewById<ImageView>(R.id.imageView)
        progressBar.visibility = View.GONE
        imageView.setImageBitmap(bmp)
        imageView.visibility = View.VISIBLE
    }
}

위 코드를 실행하면 다음과 같이 보입니다. coroutine url image loading

이 글에서 사용한 예제 코드는 GitHub - CoroutineImageProcessing를 참고해주세요.

References

Loading script...
codechachaCopyright ©2019 codechacha