Android - コルーチンでURL画像を読み込む

AndroidのコルーチンでWeb上の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

codechachaCopyright ©2019 codechacha