Picasso

똘이주인·2021년 8월 9일
0

피카소(Picasso)는 외부로부터 이미지를 불러와야 할 경우 유용하게 사용할 수 있는 라이브러리다.

매우 간단한 코드 몇 줄로 이미지 로딩, 메모리 & 디스크 캐싱, 변형(Transforming)을 가능케 한다.

장점

1. 직관적이다.

반응형 프로그래밍은 3가지 부분으로 나뉘어져 있습니다. Input → Operator → Output 딱히 복잡하게 생각하지 않아도 코드를 봤을 때, 이해할 수 있다.

2. 네트워크에서 이미지를 불러오기 간편하다.

HTTP 요청을 자동으로 만들어준다. 페이지가 어떤 종류인지 어떤 식으로 불러들여야 할지도 생각할 필요가 없다. 네트워킹을 하면서 발생하는 예외도 걱정할 필요가 없다. 메인 스레드에서 다운로드하지 않기 때문. 이미지 캐싱도 해주기 때문에 재요청한다고 해서 많은 비용이 발생하는 것도 아니다.

3. 이미지를 편집하기 쉽다.

자르거나 중심을 맞추거나, 크기를 조정하는 등의 작업은 매우 간편하게 할 수 있다.

피카소(Picasso)와 비슷한 글라이드(Glide)라는 라이브러리도 있으며, 사용법 또한 거의 같다.

다음 아티클에 어떤 차이점이 있는지 상세하게 설명되어 있으니 사용하고자 하는 시스템에 맞게 사용하면 된다.

PICASSO 사용법

// Gradle추가
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.1.0'
    testCompile 'junit:junit:4.12'
    // 여기에 하단코드 추가
		compile 'com.squareup.picasso:picasso:2.5.2'
}

매우 간단하게 imageView에 이미지를 할당할 수 있다.

ImageView imageView = (ImageView) findViewById(R.id.imageView);
Picasso.with(this)
.load("https://www.google.co.kr/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png")
.into(imageView);

Picasso(Reactive Programming)는 3부분으로 이루어져있다.
Input, Operator, Output 이렇게 3가지 Picasso에서의 Input은 with(Context) Method로 이루어져있다. Context 객체를 잘 넣으면 된다. 간단하다. Picasso에서 Output은 이미지를 보여주고 싶은 ImageView 객체를 지정해주면 된다. 중요한 건 Operator 부분이다. Picasso에서의 Operator는 많은 Method로 이루어질 수 있다. > 하단 예시

Picasso.with(context) // Input 부분
    .load(url) // Operator 시작: URL에서 이미지를 불러온다.
    .placeholder(R.drawable.user_placeholder) // 불러오는 시간 동안 보여줄 이미지 파일
    .error(R.drawable.user_placeholder_error) // 불러오지 못하면 보여주는 이미지 파일
    .resize(100, 100) // 이미지의 크기를 100x100 사이즈로 리사이즈 해준다.
    .rotate(90f) // 사진 파일을 회전해준다. Operator 끝났다.
    .into(imageView); // Output 부분: 변수 이름을 imageView라고 지정한 ImageView에 이미지를 보여준다.

기본 Bitmap포맷

1920x1080의 이미지를 768x432크기의 ImageView에서 각각 로드한경우 보여지는 이미지다.

그림을 잘 보면 Glide의 이미지화질이 Picasso보다 좋지 않음을 알 수 있다.

Picasso는 Bitmap포맷을 ARGB_8888로 사용하고 Glide는 Bitmap포맷을 RGB_565를 사용한다.
RGB_565는 ARGB_8888에 비해서 화질은 떨어지지만 메모리 용량을 50% 적게 사용한다.
래 그림은 Picasso와 Glide의 메모리 사용 그래프 비교
(8MB정도는 기본적으로 어플리케이션에서 사용하는 메모리 사용량이고 그 이상이 각각 이미지로딩 라이브러리에서 사용하는 메모리 사용량)

만약 메모리용량보다 화질이 더 중요하다고 생각한다면 Glide의 기본 Bitmap포맷을 ARGB_8888로 변경할수 있다.

GlideModule을 상속받는 클래스를 하나 지정해서 기본 포맷을 ARGB_8888로 설정하고 이 GlideModule을 매니페스트에 등록시켜주면 된다.

만약 Glide의 기본 Bitmap포맷을 ARGB8888로 변경했다면 Glide는 이전에 비해 2배정도 더 많이 메모리를 사용하는것을 볼 수 있다.

같은 Bitmap포맷을 사용하는데도 Picasso가 Glide보다 훨씬 많은 메모리 사용량을 보이고 있다.

그 이유는, Picasso는 1920x1080크기의 원본 이미지를 메모리로 가져와서 GPU에서 실시간으로 리사이징해서 768x432의 ImageView에 할당한다.

하지만 Glide는 바로 768x432크기로 메모리에 가져와서 ImageView로 할당 시키기때문에 메모리 사용량이 적다.

만약, Picasso에서도 Glide와 같은 방식을 취하고싶다면 resize()함수를 이용하면 된다.

Picasso.with(context)
		.load("http://www.selphone.co.kr/homepage/img/team/3.jpg")
		.resize(768,432)
		.into(imageView); 

//fit()함수를 사용하면 같은 효과를 볼 수 있다.
Picasso.with(context)
		.load("http://www.selphone.co.kr/homepage/img/team/3.jpg")
		.fit()
		.into(imageView); 

이미지 캐시

이미지를 캐시하는 하는 방식에서도 기본적인 정책은 Glide와 Picasso는 다르다.

위의 예시처럼 1920x1080이미지를 768x432크기의 ImageView에 로드하는경우 Glide는 768x432크기의 이미지를 캐시하는 반면, Picasso는 1920x1080의 원본 이미지를 캐시하게 된다.

만약 1920x1080 이미지를 다시 384x216크기의 ImageView로 로드한다고 할 경우 Picasso는 이미 원본 이미지를 그대로 가지고 있지만 Glide는 또하나의 384x216 크기의 이미지파일을 캐시하게 된다.

Glide는 같은 이미지를 다른 크기의 ImageView에 로드한다는 이유로 2번의 이미지 다운로드와 리사이징 작업이 필요하게 된다.

문제 1 : Fit 또는 Resize를 사용하지 못하는 경우

피카소는 해야 할 일을 모두 마친 후 (캐시, 가공 등) into 메서드를 통해 imageView에 이미지를 할당한다.

그러면 Bitmap을 사용해야 하는 경우는? 다음과 같이 Target Callback을 활용해야 한다.

Target target = new Target() {

		@Override
		public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
				doSomething(bitmap)
		}

		@Override
		public void onBitmapFailed(Drawable errorDrawable) {
		
		}
		
		@Override
		public void onPrepareLoad(Drawable placeHolderDrawable) {
	
		}
};

Picasso
		.with(this)
		.load("http://i.imgur.com/DvpvklR.png")
		.into(target);

그런데, Target Callback을 통해 bitmap을 전달받는 경우는 resize, centerCrop, fit 등 몇몇 함수를 사용할 수 없다.
만약 함께 사용하려 시도한다면 다음 에러가 발생한다.

Caused by: java.lang.IllegalStateException: Fit cannot be used with a Target.

개발할 때, Bitmap을 사용해야 하는 경우는 다음과 같았는데

  • Notification의 thumbnail
  • 락 스크린의 미디어 thumbnail

피카소 라이브러리의 Fit 함수 구현체를 보면 다음과 같이 기술되어 있다.

/**
 * Attempt to resize the image to fit exactly into the target {@link ImageView}'s bounds. This
 * will result in delayed execution of the request until the {@link ImageView} has been laid out.
 * Note: This method works only when your target is an {@link ImageView}.
 */
public RequestCreator fit() {
  deferred = true;
  return this;
}

Resize, centerCrop 등의 함수를 사용할 수 없으니, 정사각형 1:1 비율이 아닌 이미지의 경우는 어쩔 수 없이 아래처럼 Bitmap을 후가공하는 커스텀 함수를 구현하여 아래와 같이 처리

Transformation transformation = new Transformation() { 
		@Override
		public Bitmap transform(Bitmap source) {
				return myCenterCrop(source);
		}

		@Override
		public String key() {
				return null;
		}
}

Picasso
		.with(this)
		.load(url)
		.transform(transformation)
		.into(target);

문제 2 : 메모리 증가 문제

onBitmapLoaded를 통해 넘어온 bitmap을 가공해서 사용할 경우, 가공된 bitmap을 캐시할 수 없기 때문에 계속해서 메모리 누수가 발생하는 것 (가공된 bitmap을 캐시하도록 따로 추가한다면 모르지만)

해결방법

이를 해결하기 위해서는 다음과 같이 onBitmapLoaded로 넘어오기 이전에 이미지 처리를 해야 가공된 이미지를 메모리에 올릴 수 있다.

Transformation transformation = new Transformation() {
		@Override
		public Bitmap transform(Bitmap source) {
				return myCenterCrop(source);
		}

		@Override
		public String key() {
				return null;
		}
}

Picasso
		.with(this)
		.load(url)
		.transform(transformation)
		.into(target);

이후, transform 함수 내에서 로그를 찍어보면 최초 한 번만 호출되는 것을 확인할 수 있고, 더불어 메모리 누수도 막을 수 있다.

0개의 댓글