Android 로드 큰 그림 및 다 중 그림 프로그램 OOM(OutOFMemory)이상 발생 방지
1.고 효율 로 딩 이미지
우 리 는 안 드 로 이 드 프로그램 을 작성 할 때 많은 그림 을 사용 합 니 다.그림 마다 모양 이 다 르 고 크기 가 다 르 지만 대부분의 경우 이 그림 들 은 우리 프로그램 이 필요 로 하 는 크기 보다 큽 니 다.예 를 들 어 시스템 갤러리 에 전 시 된 사진 은 대부분 핸드폰 카메라 로 찍 힌 것 으로 이런 사진 들 의 해상 도 는 우리 핸드폰 화면의 해상도 보다 훨씬 높다.우리 가 작성 한 프로그램 은 모두 일정한 메모리 제한 이 있 기 때문에 프로그램 이 너무 높 은 메모 리 를 차지 하면 OOM(OutOf Memory)이상 이 발생 하기 쉽다 는 것 을 잘 알 아야 한다.우 리 는 아래 의 코드 를 통 해 모든 프로그램 이 가장 많이 사용 할 수 있 는 메모리 가 얼마 인지 알 수 있다.
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
Log.d("TAG", "Max memory is " + maxMemory + "KB");
따라서 고해상도 그림 을 전시 할 때 는 먼저 그림 을 압축 하 는 것 이 좋다.압축 된 그림 의 크기 는 그것 을 보 여 주 는 컨트롤 의 크기 와 비슷 해 야 한다.아주 작은 ImageView 에 큰 그림 을 표시 하 는 것 은 어떠한 시각 적 이점 도 가 져 오지 않 지만 우리 의 상당히 많은 귀중 한 메모 리 를 차지 할 뿐만 아니 라 성능 에 도 부정적인 영향 을 미 칠 수 있다.큰 그림 을 어떻게 적당 한 압축 하여 가장 좋 은 크기 로 표시 할 수 있 는 지 살 펴 보 는 동시에 OOM 의 출현 을 방지 할 수 있 습 니 다.그림 압축 은 이 종류의 BitmapFactory 를 사용 해 야 합 니 다.더 알 고 싶 으 면 다른 글 로 이동 하 십시오Bitmap 상세 설명 과 Bitmap 의 메모리 최적화.
BitmapFactory 라 는 종 류 는 여러 가지 해석 방법(decodeByteArray,decodeFile,decodeResource 등)을 제공 하여 Bitmap 대상 을 만 드 는 데 사용 합 니 다.그림 의 출처 에 따라 적당 한 방법 을 선택해 야 합 니 다.예 를 들 면:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
OOM 이상 을 피하 기 위해 서 는 모든 그림 을 분석 할 때 그림 의 크기 를 확인 하 는 것 이 좋 습 니 다.그림 의 출처 를 믿 지 않 는 한 프로그램의 사용 가능 한 메모 리 를 초과 하지 않도록 하 는 것 이 좋 습 니 다.현재 그림 의 크기 는 이미 알 고 있 습 니 다.우 리 는 전체 그림 을 메모리 에 불 러 올 지,아니면 압축 판 그림 을 메모리 에 불 러 올 지 결정 할 수 있 습 니 다.다음 과 같은 몇 가지 요 소 는 우리 가 고려 해 야 할 것 이다.
그러면 우 리 는 어떻게 해야만 그림 을 압축 할 수 있 습 니까?BitmapFactory.Options 에서 inSampleSize 의 값 을 설정 하면 이 루어 집 니 다.예 를 들 어 우 리 는 2048*1536 픽 셀 의 그림 이 있 는데 inSampleSize 의 값 을 4 로 설정 하면 이 그림 을 512*384 픽 셀 로 압축 할 수 있다.원래 이 그림 을 불 러 오 려 면 13M 의 메모 리 를 사용 해 야 하 는데 압축 하면 0.75M 만 사용 해 야 합 니 다.(그림 이 ARGB 라 고 가정 하면8888 형식,즉 픽 셀 점 당 4 개의 바이트 차지).다음 방법 은 들 어 오 는 너비 와 높이 에 따라 적합 한 inSampleSize 값 을 계산 할 수 있 습 니 다.
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
//
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
//
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// inSampleSize ,
// 。
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
이 방법 을 사용 하려 면 먼저 BitmapFactory.Options 의 inJustDecodeBounds 속성 을 true 로 설정 하고 그림 을 분석 해 야 합 니 다.그리고 BitmapFactory.Options 를 원 하 는 너비 와 높이 와 함께 calculate InSampleSize 방법 에 전달 하면 적당 한 inSampleSize 값 을 얻 을 수 있 습 니 다.이후 그림 을 한 번 더 분석 하고 새로 가 져 온 inSampleSize 값 을 사용 하 며 inJustDecodeBounds 를 false 로 설정 하면 압축 된 그림 을 얻 을 수 있 습 니 다.
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// inJustDecodeBounds true,
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// inSampleSize
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
아래 코드 는 임의의 그림 을 100*100 미리 보기 그림 으로 간단하게 압축 하여 ImageView 에 보 여 줍 니 다.
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
2.이미지 캐 시 기술 사용응용 프로그램의 UI 인터페이스 에 그림 을 한 장 불 러 오 는 것 은 매우 간단 한 일이 지만,화면 에 많은 그림 을 불 러 올 필요 가 있 을 때 상황 은 복잡 해진 다.많은 경우(예 를 들 어 ListView,GridView 또는 ViewPager 와 같은 구성 요 소 를 사용)화면 에 표 시 된 그림 은 화면 을 미 끄 러 뜨리 는 등 사건 을 통 해 계속 증가 하여 결국은 OOM 을 초래 할 수 있다.
메모리 의 사용 이 항상 합 리 적 인 범 위 를 유지 하도록 하기 위해 서 는 화면 에서 제 거 된 그림 을 회수 처리 합 니 다.이때 쓰레기 수 거 기 는 이 그림 들 의 인용 을 더 이상 가지 고 있 지 않다 고 생각 하고 이 그림 들 을 GC 로 조작 할 것 이다.이런 사고방식 으로 문 제 를 해결 하 는 것 은 매우 좋 은 일이 다.그러나 프로그램 이 신속하게 실 행 될 수 있 도록 인터페이스 에 그림 을 신속하게 불 러 오기 위해 서 는 일부 그림 이 회 수 된 후에 사용자 가 다시 화면 에 미 끄 러 지 는 상황 을 고려 해 야 한다.이때 방금 불 러 온 그림 을 다시 불 러 오 는 것 은 성능 의 병목 임 에 틀림없다.이 상황 을 피 할 방법 을 강구 해 야 한다.
이 럴 때 메모리 캐 시 기술 을 사용 하면 이 문 제 를 잘 해결 할 수 있 습 니 다.구성 요소 가 그림 을 빠르게 다시 불 러 오고 처리 할 수 있 습 니 다.메모리 캐 시 기술 을 사용 하여 그림 을 캐 시 하 는 방법 을 살 펴 보 겠 습 니 다.프로그램 이 많은 그림 을 불 러 올 때 응답 속도 와 유창 성 을 높 일 수 있 습 니 다.
메모리 캐 시 기술 은 프로그램의 귀중 한 메모 리 를 대량으로 사용 하 는 그림 에 빠 른 접근 방법 을 제공 합 니 다.그 중에서 가장 핵심 적 인 종 류 는 LruCache 입 니 다.이 종 류 는 그림 을 캐 시 하 는 데 매우 적합 합 니 다.주요 알고리즘 원 리 는 최근 에 사용 한 대상 을 링크 드 HashMap 에 강 한 인용 으로 저장 하고 최근 에 가장 적 게 사용 한 대상 을 캐 시 값 이 미리 설정 되 기 전에 메모리 에서 제거 하 는 것 입 니 다.
과거 에 우 리 는 매우 유행 하 는 메모리 캐 시 기술 의 실현,즉 소프트 참조 또는 약 한 참조(SoftReference or WeakReference)를 자주 사용 했다.하지만 이 제 는 이런 방식 을 추천 하지 않 는 다.안 드 로 이 드 2.3(API Level 9)부터 쓰레기 수 거 기 는 소프트 인용 이나 약 한 인용 을 가 진 대상 을 회수 하 는 경향 이 강해 소프트 인용 과 약 한 인용 을 신뢰 할 수 없 게 만 들 기 때문이다.또한,Android 3.0(API Level 11)에 서 는 그림 의 데이터 가 로 컬 메모리 에 저장 되 기 때문에 예측 가능 한 방식 으로 방출 할 수 없습니다.이 는 잠재 적 인 위험 으로 인해 프로그램의 메모리 가 넘 치고 붕 괴 될 수 있 습 니 다.
LruCache 에 적당 한 캐 시 크기 를 선택 할 수 있 도록 다음 과 같은 여러 가지 요 소 를 고려 범위 에 넣 어야 합 니 다.예 를 들 어:
그림 을 캐 시 하려 면 LruCache 를 사용 하 는 예 입 니 다.
private LruCache<String, Bitmap> mMemoryCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
// , OutOfMemory 。
// LruCache , KB 。
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// 1/8 。
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// , 。
return bitmap.getByteCount() / 1024;
}
};
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
이 예 에서 시스템 이 프로그램 에 분 배 된 8 분 의 1 의 메모 리 를 캐 시 크기 로 사용 합 니 다.중고 사양 의 휴대 전화 에는 4 조(32/8)정도 의 캐 시 공간 이 있다.전체 화면의 GridView 는 800 x480 해상도 그림 4 장 으로 채 우 면 약 1.5 메 가 의 공간(800*480*4)을 차지 합 니 다.따라서 이 캐 시 크기 는 2.5 페이지 의 그림 을 저장 할 수 있다.ImageView 에 그림 을 한 장 불 러 올 때,먼저 LruCache 캐 시 에서 검 사 를 합 니 다.해당 키 값 을 찾 으 면 바로 ImageView 를 업데이트 합 니 다.그렇지 않 으 면 배경 스 레 드 를 열 어 이 그림 을 불 러 옵 니 다.
public void loadBitmap(int resId, ImageView imageView) {
final String imageKey = String.valueOf(resId);
final Bitmap bitmap = getBitmapFromMemCache(imageKey);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
imageView.setImageResource(R.drawable.image_placeholder);
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
task.execute(resId);
}
}
BitmapWorkerTask 는 새로 불 러 온 그림 의 키 값 을 캐 시 에 맞 춰 야 합 니 다.
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
// 。
@Override
protected Bitmap doInBackground(Integer... params) {
final Bitmap bitmap = decodeSampledBitmapFromResource(
getResources(), params[0], 100, 100);
addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
return bitmap;
}
}
상기 두 가지 방법 을 익 혔 습 니 다.프로그램 에 큰 그림 을 불 러 오 든 대량의 그림 을 불 러 오 든 OOM 의 문 제 를 걱정 하지 마 세 요!읽 어 주 셔 서 감사합니다. 여러분 에 게 도움 이 되 기 를 바 랍 니 다.본 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.