Android 로드 큰 그림 및 다 중 그림 프로그램 OOM(OutOFMemory)이상 발생 방지

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 대상 을 만 드 는 데 사용 합 니 다.그림 의 출처 에 따라 적당 한 방법 을 선택해 야 합 니 다.예 를 들 면:
  • SD 카드 의 그림 은 decodeFile 방법
  • 을 사용 할 수 있다.
  • 네트워크 에 있 는 사진 은 decodeStream 방법
  • 을 사용 할 수 있다.
  • 자원 파일 의 그림 은 decodeResource 방법 을 사용 할 수 있 습 니 다.
  • 이 방법 들 은 이미 구 축 된 bitmap 에 메모 리 를 할당 하려 고 시도 합 니 다.이 경우 OOM 이 나타 나 기 쉽 습 니 다.이 를 위해 모든 분석 방법 은 선택 할 수 있 는 BitmapFactory.Options 인 자 를 제공 합 니 다.이 매개 변수의 inJustDecodeBounds 속성 을 true 로 설정 하면 분석 방법 이 bitmap 에 메모 리 를 분배 하 는 것 을 금지 할 수 있 습 니 다.반환 값 도 Bitmap 대상 이 아니 라 null 입 니 다.비트 맵 은 null 이지 만 비트 맵 Factory.Options 의 outWidth,outHeight,outMime Type 속성 은 모두 대 입 됩 니 다.이 기법 은 그림 을 불 러 오기 전에 그림 의 길이 와 너비,MIME 형식 을 얻 고 상황 에 따라 그림 을 압축 할 수 있 습 니 다.다음 코드 와 같이:
    
    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 이상 을 피하 기 위해 서 는 모든 그림 을 분석 할 때 그림 의 크기 를 확인 하 는 것 이 좋 습 니 다.그림 의 출처 를 믿 지 않 는 한 프로그램의 사용 가능 한 메모 리 를 초과 하지 않도록 하 는 것 이 좋 습 니 다.
    현재 그림 의 크기 는 이미 알 고 있 습 니 다.우 리 는 전체 그림 을 메모리 에 불 러 올 지,아니면 압축 판 그림 을 메모리 에 불 러 올 지 결정 할 수 있 습 니 다.다음 과 같은 몇 가지 요 소 는 우리 가 고려 해 야 할 것 이다.
  • 그림 전 체 를 불 러 오 는 데 필요 한 메모 리 를 예상 합 니 다.
  • 이 그림 을 불 러 오기 위해 얼마나 많은 메모 리 를 제공 하고 싶 습 니까?
  • 이 그림 을 보 여 주 는 컨트롤 의 실제 크기 입 니 다.
  • 현재 장치 의 화면 크기 와 해상도.
  • 예 를 들 어 이미지 뷰 는 128*96 픽 셀 의 크기 만 있 습 니 다.미리 보기 그림 을 표시 하기 위해 서 입 니 다.이때 1024*768 픽 셀 의 그림 을 메모리 에 완전히 불 러 오 는 것 은 가치 가 없습니다.
    그러면 우 리 는 어떻게 해야만 그림 을 압축 할 수 있 습 니까?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 에 적당 한 캐 시 크기 를 선택 할 수 있 도록 다음 과 같은 여러 가지 요 소 를 고려 범위 에 넣 어야 합 니 다.예 를 들 어:
  • 당신 의 장 치 는 모든 프로그램 에 얼마나 큰 메모 리 를 분배 할 수 있 습 니까?
  • 장치 화면 에 최대 몇 장의 그림 이 표 시 됩 니까?화면 에 도 곧 표 시 될 수 있 기 때문에 미리 불 러 와 야 할 그림 이 얼마나 있 습 니까?4567918)
  • 장치 의 화면 크기 와 해상 도 는 각각 얼마 입 니까?초고 해상도 장치(예 를 들 어 갤 럭 시 넥 서 스)는 낮은 해상도 장치(예 를 들 어 넥 서 스 S)보다 같은 양의 그림 을 가지 고 있 을 때 더 큰 캐 시 공간 이 필요 합 니 다.
  • 그림 의 크기 와 크기,그리고 그림 마다 얼마나 많은 메모리 공간 을 차지 합 니까?
  • 사진 이 방문 하 는 빈 도 는 얼마나 됩 니까?일부 그림 의 접근 빈도 가 다른 그림 보다 높 지 않 습 니까?있 으 면 일부 그림 을 메모리 에 상주 시 키 거나 여러 개의 LruCache 대상 을 사용 하여 다른 그룹의 그림 을 구분 해 야 할 수도 있 습 니 다.
  • 당신 은 수량 과 품질 간 의 균형 을 잘 유지 할 수 있 습 니까?어떤 때 는 낮은 픽 셀 의 그림 을 여러 개 저장 하고 배경 에서 스 레 드 를 열 어 높 은 픽 셀 의 그림 을 불 러 오 는 것 이 더욱 효과 적 입 니 다.
  • 모든 프로그램 을 만족 시 킬 수 있 는 지정 한 캐 시 크기 가 없습니다.이것 은 당신 이 결정 한 것 입 니 다.너 는 프로그램 메모리 의 사용 상황 을 분석 한 후에 적당 한 해결 방안 을 제정 해 야 한다.너무 작은 캐 시 공간 으로 인해 그림 이 자주 풀 리 고 다시 불 러 올 수 있 습 니 다.이것 은 좋 은 점 이 없습니다.너무 큰 캐 시 공간 은 자바.lang.OutOf Memory 의 이상 을 일 으 킬 수 있 습 니 다.
    그림 을 캐 시 하려 면 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 의 문 제 를 걱정 하지 마 세 요!
    읽 어 주 셔 서 감사합니다. 여러분 에 게 도움 이 되 기 를 바 랍 니 다.본 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!

    좋은 웹페이지 즐겨찾기