안녕하세요, 이륙사입니다.
최근에 프로그래머스 과제관에서 연습을 하던 중 문자열로된 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) {
// 에러 처리
}
}