본문 바로가기
Android App/Kotlin

코루틴(Coroutine)-구현하기(이미지 다운로드 앱)

by AppJinny 2022. 11. 30.

*코루틴(Coroutine)-구현하기(이미지 다운로드 앱)

-build.gradle

-- android{} 에 뷰바인딩 추가

//뷰바인딩
buildFeatures{
    viewBinding true
}

 

-AndroidManifest.xml

<!--  인터넷 권한 선언  -->
<!--  <manifest> 태그 안에 인터넷 권한 선언하고
<!--  http주소에 대해 강화된 네트워크 보안정책으로 인한 오류 예외처리를 위해  -->
<!--  android:usesCleartextTraffic="true" 설정  -->

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

    <application

        android:usesCleartextTraffic="true"

 

-activity_main.xml 

<?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/imagePreview"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginBottom="8dp"
        android:layout_marginTop="16dp"
        app:layout_constraintBottom_toTopOf="@+id/editUrl"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

<!--  라인을 한줄로 변경하고 개행되지 않게 InputType설정 필수  -->
    <EditText
        android:id="@+id/editUrl"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:hint="여기에 URL을 입력하세요"
        android:maxLines="1"
        android:inputType="text"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/btnDownload"
        app:layout_constraintStart_toStartOf="parent" />

    <Button
        android:id="@+id/btnDownload"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginBottom="16dp"
        android:backgroundTint="@color/teal_700"
        android:text="다운로드"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <ProgressBar
        android:id="@+id/progress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="@+id/imagePreview"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/imagePreview" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

-MainActivity.kt

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import com.heeyjinny.coroutine.databinding.ActivityMainBinding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.net.URL

//인터넷 주소를 넣으면 이미지를 불러오는 앱 생성
//1
//인터넷 이미지를 다운로드 하기 위해 인터넷 권한 선언필요
//AndroidManifest.xml 에서 권한 설정

//2
//탑레벨 함수 작성
//suspend키워드를 사용해 코루틴을 만들어 함수 loadImage()생성
//imageUrl을 문자열로 파라미터로 받아 Bitmap형식으로 리턴하는 함수
suspend fun loadImage(imageUrl: String): Bitmap {
    //2-1
    //파라미터로 전달받은 주소로 URL객체 생성
    val url = URL(imageUrl)
    //2-2
    //URL이 가지고 있는 openStream의 변수 생성
    val stream = url.openStream()
    //2-3
    //주소를 열어 비트맵 이미지 저장한 값을 반환
    return BitmapFactory.decodeStream(stream)
}

class MainActivity : AppCompatActivity() {

    //뷰바인딩
    val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        //3
        //다운로드 버튼을 클릭하면 주소를 전달해 이미지를 보여주는 코드 생성
        binding.btnDownload.setOnClickListener {
            //3-1
            //코루틴 스코프 추가,
            //디스패처는 Main으로 입력해 UI관련 요소들을 다룰 수 있도록 구성
            CoroutineScope(Dispatchers.Main).launch {
                //3-2
                //버튼이 클릭되면 가장먼저 코루틴 실행
                //버튼이 클릭되면 프로그래스바가 화면에 보여지도록 설정
                binding.progress.visibility= View.VISIBLE

                //3-3
                //에디트텍스트에 입력된 값 문자열로 가져와 변수url에 저장
                val url = binding.editUrl.text.toString()

                //3-4
                //loadImage()함수를 호출하면서 url전달
                //백그라운드를 처리하는 IO컨텍스트에서 진행되어야함
                //withContext()문을 사용해 컨텍스트를 IO로 전환하고 변수bitmap에 저장
                val bitmap = withContext(Dispatchers.IO){
                    loadImage(url)
                }

                //3-5
                //loadImage()함수에서 비트맵 생성하여
                //변수bitmap에 저장될 때까지 다음줄이 실행되지않고 멈춤
                //이미지뷰에 변수bitmap의 값 입력
                binding.imagePreview.setImageBitmap(bitmap)

                //3-6
                //프로그래스바는 다시 안보이게 설정
                binding.progress.visibility = View.GONE

            }//코루틴스코프

        }//btnDownload

        //4
        //바인딩 처리를 run스코프로 감싸면 반복되는 binding.을 제거할 수 있음
        binding.run {

            btnDownload.setOnClickListener {

                CoroutineScope(Dispatchers.Main).launch {

                    progress.visibility= View.VISIBLE

                    val url = editUrl.text.toString()

                    val bitmap = withContext(Dispatchers.IO){
                        loadImage(url)
                    }

                    imagePreview.setImageBitmap(bitmap)
                    progress.visibility = View.GONE

                }//코루틴스코프
            }//btnDownload
        }//바인딩 run

    }//onCreate
}//MainActivity

 

-결과

 

 

 


이 포스팅에 작성한 내용은 고돈호, ⌜이것이 안드로이드다⌟, 한빛미디어(주), 2022 에서 발췌하였습니다.