반응형

웹서버에 데이터를 요청하고 응답을 받아 처리하는 과정을 해보았습니다.

그런데 응답으로 받을 데이터가 이미지 파일인 경우에는 조금 다른 방식으로 처리할 필요가 있습니다.

왜냐하면 이미지 파일 하나의 크기가 1메가바이트가 넘는 경우가 많아지면서 이미지 파일을 다른 데이터와 구분하여 받는 방식이 더 효율적이기 때문입니다.

따라서 이미지 파일에 대한 정보만 응답 데이터에 넣어두고 이미지 파일은 별도로 다운로드하게 됩니다.

그러면 이미지 파일을 어떻게 다운로드할 수 있는지 살펴보고 코드를 만들어봅시다.



이미지 다운로드

이미지 파일이 웹서버의 어디에 위치하고 있는지에 대한 정보를 받았다면 그 파일을 다운로드할 수 있습니다.

인터넷을 사용해야 하므로 인터넷 권한을 추가하고 스레드를 사용해야 합니다.

따라서 직접 코드를 만들지 않고 외부 라이브러리를 사용하는 경우도 많습니다.

UniversalImageLoader와 같은 외부 라이브러리는 간단하게 이미지를 다운로드할 수 있도록 도와줍니다.

하지만 여기에서는 직접 이미지를 다운로드하는 코드를 만들어봅니다.

때로는 직접 코드를 만들어보는 것이 이해하는 데 더 도움이 되기 때문이죠.

 

AsyncTask로 이미지 다운로드하기

스레드를 사용하기 위해 AsyncTask를 상속하여 새로운 클래스를 정의합니다.

이 클래스를 이용해 객체를 생성할 때는 이미지가 있는 주소와 함께 이 이미지를 다운로드받은 후 화면에 보여줄 때 사용할 이미지뷰(ImageView) 객체를 파라미터로 전달합니다.

public class ImageLoadTask extends AsyncTask<Void, Void, Bitmap> {
    private String urlStr;
    private ImageView imageView;

    public ImageLoadTask(String urlStr, ImageView imageView) {
        this.urlStr = urlStr;
        this.imageView = imageView;
    }

스레드 내에서 동작하는 doInBackground 메소드 안에서는 웹서버의 이미지 데이터를 받아 비트맵 객체로 만들어줍니다.

BitmapFactory 클래스의 decodeStream 메소드를 사용하면 간단한 코드 만으로도 비트맵 객체를 만들어줄 수 있습니다.

@Override
protected Bitmap doInBackground(Void... voids) {
    Bitmap bitmap = null;
    try {
        URL url = new URL(urlStr);
        bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream());     } catch(Exception e) {
        e.printStackTrace();
  }

  return bitmap;
}

비트맵 객체로 변환하고 나면 메인 스레드에서 이미지뷰에 표시합니다.

onPostExecute 메소드 안에 다음과 같이 넣어줍니다.

@Override
protected void onPostExecute(Bitmap bitmap) {
    super.onPostExecute(bitmap);

    imageView.setImageBitmap(bitmap);
    imageView.invalidate();
}

코드의 양이 그리 많지 않죠?

 

비트맵 객체의 메모리 해제

비트맵 객체는 메모리에 만들어진 후 해제되지 않으면 메모리에 계속 남아있게 됩니다.

자바의 Garbage Collection 메커니즘을 이용해 메모리에서 해제될 수 있지만 앱에서 여러 이미지를 로딩하게 되면 메모리가 부족해지는 문제가 발생할 수 있으므로 사용하지 않는 비트맵 객체는 recycle 메소드를 이용해 즉시 해제시키는 것이 필요합니다.

AsyncTask를 상속하여 만든 클래스 안에서 이미지를 다운로드하여 비트맵 객체로 만드는 경우 동일한 비트맵 객체를 요청한다면 다운로드한 이미지 파일을 로컬에 저장했다가 재사용할 수도 있고 아니면 이전 비트맵 객체를 메모리에서 해제한 후 새로 다운로드할 수도 있습니다.

여기에서는 이전 비트맵 객체를 메모리에서 해제한 후 새로 다운로드하는 방법으로 만들어봅니다.

클래스 안에 HashMap 객체를 만들고 이미지의 주소를 메모리에 만들어진 비트맵 객체와 매핑되도록 해 줍니다.

public class ImageLoadTask extends AsyncTask<Void, Void, Bitmap> {
    private String urlStr;
    private ImageView imageView;

    private static HashMap<String, Bitmap> bitmapHash = new HashMap<String, Bitmap>();

이미지 데이터를 이용해 비트맵 객체를 만들었을 때는 해시테이블에 그 객체를 추가합니다.

그리고 새로운 비트맵 객체를 만들기 전에는 항상 해시테이블 안에 동일한 주소를 요청하는 경우에 이전에 만들어졌던 비트맵 객체를 메모리에서 해제하도록 합니다.

@Override
protected Bitmap doInBackground(Void... voids) {
    Bitmap bitmap = null;
    try {
        if (bitmapHash.containsKey(urlStr)) {
            Bitmap oldBitmap = bitmapHash.remove(urlStr);
            if (oldBitmap != null) {
                oldBitmap.recycle();
                oldBitmap = null;
            }
        }

        URL url = new URL(urlStr);
        bitmap = BitmapFactory.decodeStream(url.openConnection().getInputStream());

        bitmapHash.put(urlStr, bitmap);
    } catch(Exception e) {
        e.printStackTrace();
    }

    return bitmap;
}

 

반응형

'안드로이드 개발 > 부스트코스(안드로이드 프로그래밍)' 카테고리의 다른 글

데이터베이스와 테이블 만들기  (0) 2019.03.20
데이터베이스와 SQL  (0) 2019.03.20
JSON GSON  (0) 2019.03.19
volley  (0) 2019.03.19
웹으로 요청하기  (0) 2019.03.18
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기