안녕하세요, 이륙사입니다.

최근에 프로그래머스 과제관에서 연습을 하던 중 문자열로된 url을 Bitmap으로 변환해야 하는 일이 있었습니다. 간단하지만 공유하면 좋을 것 같아서 포스팅하려고 합니다.

1. URL -> InputStream -> Bitmap 변환

URL을 InpustSream으로 바꾸는 부분이 핵심입니다.

방법1) URL.openstream() 사용

private fun convertStringToBitmap(urlString: URL): Bitmap {
    val inputStream = url.openStream()
    return BitmapFactory.decodeStream(inputStream) // Bitmap
}

방법2) HttpURLConnection 사용

private fun convertUrlToBitmap(url: URL): Bitmap {
    val connection = url.openConnection() as HttpURLConnection
    connection.doInput = true
    connection.connect()

    return BitmapFactory.decodeStream(connection.inputStream)
}

2. ImageView에 적용

fun setImageFromUrl(imageView: ImageView, urlString: String) {
    val url = URL(urlString)
    val bitmap = convertUrlToBitmap(url)
    imageView.setImageBitmap(bitmap)
}

이렇게 하면.. 짠! NetworkOnMainThreadException가 발생합니다. url의 stream을 여는 과정에서 네트워크가 사용되는데, 이것을 메인 스레드에서 동작시켰기 때문입니다. 저는 코루틴을 통해 백그라운드 스레드에서 동작시켜 이를 해결하겠습니다.

fun setImageFromUrl(imageView: ImageView, urlString: String) {
   GlobalScope.launch(Dispatchers.IO) { // 백그라운드 스레드
      val url = URL(urlString)
      val bitmap = convertUrlToBitmap(url)
      imageView.setImageBitmap(bitmap)
   }
}

그리고 동작시켜보면 또 에러가 납니다..ㅋㅋㅋ imageView를 백그라운드 스레드에서 변경하려고 했기 때문이죠. 이것만 메인 스레드에서 동작시키면 정말로 끝이 납니다.

fun setImageFromUrl(imageView: ImageView, urlString: String) {
   GlobalScope.launch(Dispatchers.IO) {
      val url = URL(urlString)
      val bitmap = convertUrlToBitmap(url) // 네트워크는 백그라운드 스레드에서,
      withContext(Dispatchers.Main){ // UI는 메인 스레드에서
         imageView.setImageBitmap(bitmap)
      }
   }
}

최종 코드

GlobalScope.launch(Dispatchers.IO) {
    try {
        val url = URL(urlString)
        val bitmap = convertUrlToBitmap(url)

        withContext(Dispatchers.Main) {
            completed(bitmap)
        }
    } catch (e: Exception) {
        // 에러 처리
    }
}

 

+ Recent posts