Android 이미지 캐 시 Lru 알고리즘(2)

선언:
전편 에서 우 리 는 Bitmap 의 처 리 를 총 결 했 고 각종 처리 의 효율 과 메모리 점용 크기 를 비교 했다.클릭 하여 보기많은 그림 을 사용 하면 OOM(out of memory)을 초래 할 수 있다 는 것 을 알 게 되 었 습 니 다.어떻게 처리 해 야 oom 이 발생 할 확률 을 낮 출 수 있 습 니까?이전에 우 리 는 SoftReference 소프트 인용 을 사용 해 왔 습 니 다.SoftReference 는 이제 더 이상 추천 하지 않 는 방식 입 니 다.Android 2.3(API Level 9)부터 쓰레기 수 거 기 는 소프트 인용 이나 약 한 인용 을 가 진 대상 을 회수 하 는 경향 이 있 기 때문에 소프트 인용 을 더 이상 신뢰 할 수 없 게 만 들 기 때문에 오늘 우 리 는 새로운 캐 시 처리 알고리즘 인 Lru 를 알 게 되 었 습 니 다.그리고 Lru 기반 의 Lrucache,DiskLruCache 를 배 워 서 우리 의 이미지 캐 시 를 실현 합 니 다. 
Lru:
LRU 는 Least Recently Used 의 줄 임 말 입 니 다.번역 하면'최근 에 가장 적 게 사용 합 니 다'입 니 다.LRU 캐 시 는 이런 원 리 를 사용 하여 이 루어 집 니 다.쉽게 말 하면 일 정량의 데 이 터 를 캐 시 하고 설 정 된 한도 값 을 초과 할 때 만 료 된 데 이 터 를 삭제 합 니 다.예 를 들 어 우리 가 10000 개의 데 이 터 를 캐 시 하고 데이터 가 10000 개 이하 일 때 임의로 추가 할 수 있 습 니 다.10000 개가 넘 으 면 새로운 데 이 터 를 추가 하고 만 료 된 데 이 터 를 삭제 하여 최대 캐 시 10000 개 를 확보 해 야 합 니 다.만 료 된 데 이 터 를 어떻게 삭제 하 는 지 확인 합 니까?LRU 알고리즘 을 사용 하면 가장 오래된 데 이 터 를 삭제 하 는 것 입 니 다.
LruCache 기반 메모리 캐 시 구현:
1.)Memory Cache 초기 화
여기 메모리 캐 시 는 Bitmap 가 아 닌 Drawable 입 니 다.왜냐하면 Drawable 은 Bitmap 에 비해 메모리 장점 이 많 기 때 문 입 니 다.        

 int maxMemory = (int) Runtime.getRuntime().maxMemory();//               
 int mCacheSize = maxMemory / 8;//              
 mMemoryCache = new LruCache<String, Drawable>(mCacheSize) {
  //       ,   Bitmap   
  @Override
  protected int sizeOf(String key, Drawable value) {
  if (value instanceof BitmapDrawable) {
   Bitmap bitmap = ((BitmapDrawable) value).getBitmap();
   return bitmap == null ? 0 : bitmap.getByteCount();
  }
  return super.sizeOf(key, value);
  }
 };
2.)메모리 캐 시 에 Drawable 을 추가 합 니 다. 

 /**
 *   Drawable     
 *
 * @param key
 * @param drawable
 */
 private void addDrawableToMemoryCache(String key, Drawable drawable) {
 if (getDrawableFromMemCache(key) == null && drawable != null) {
  mMemoryCache.put(key, drawable);
 }
 }

3.)메모리 캐 시 에서 Drawable 가 져 오기

 /**
 *           Drawable
 *
 * @param key
 * @return
 */
 public Drawable getDrawableFromMemCache(String key) {
 return mMemoryCache.get(key);
 }

4.)메모리 캐 시 에서 Drawable 을 삭제 합 니 다.

 /**
 *         
 *
 * @param key
 */
 public void removeCacheFromMemory(String key) {
 mMemoryCache.remove(key);
 }

5.)메모리 캐 시 비우 기

 /**
 *       
 */
 public void cleanMemoryCCache() {
 mMemoryCache.evictAll();
 } 
사실 Lru 캐 시 메커니즘 은 본질 적 으로 하나의 LinkedHashMap 에 저장 되 어 삽 입 된 데이터 순 서 를 보장 하고 정리 하기 편리 하 다. 
DiskLruCache 기반 디스크 캐 시 구현:
DiskLruCache 클래스 는 구 글 이 공식 적 으로 실현 하 는 것 이 아니 라 자체 다운로드,다운로드 주소 가 필요 합 니 다.https://github.com/JakeWharton/DiskLruCache
1.)DiskLruCache 초기 화 

 File cacheDir = context.getCacheDir();//           
 long diskCacheSize = 1024 * 1024 * 30;//             
 int appVersion = DiskLruUtils.getAppVersion(context);//            
 int valueCount = 1;//     key           
 try {
  mDiskCache = DiskLruCache.open(cacheDir, appVersion, valueCount, diskCacheSize);
 } catch (Exception ex) {
 }
2.)디스크 캐 시 에 파일 쓰기 

 /**
 *   Bitmap     
 *
 * @param key
 * @param value
 */
 private void addBitmapToDiskCache(String key, byte[] value) {
 OutputStream out = null;
 try {
  DiskLruCache.Editor editor = mDiskCache.edit(key);
  if (editor != null) {
  out = editor.newOutputStream(0);
  if (value != null && value.length > 0) {
   out.write(value);
   out.flush();
   editor.commit();
  } else {
   editor.abort();
  }
  }
  mDiskCache.flush();
 } catch (IOException e) {
  e.printStackTrace();
 } finally {
  DiskLruUtils.closeQuietly(out);
 }
 }
3.)디스크 캐 시 에서 Drawable 읽 기 

 /**
 *           Drawable
 *
 * @param key
 * @return
 */
 public Drawable getDrawableFromDiskCache(String key) {
 try {
  DiskLruCache.Snapshot snapShot = mDiskCache.get(key);
  if (snapShot != null) {
  InputStream is = snapShot.getInputStream(0);
  Bitmap bitmap = BitmapFactory.decodeStream(is);
  Drawable drawable = DiskLruUtils.bitmap2Drawable(bitmap);
  //                
  addDrawableToMemoryCache(key, drawable);
  return drawable;
  }
 } catch (IOException e) {
  e.printStackTrace();
 }
 return null;
 }

4.)디스크 캐 시 에서 삭제

 /**
 *         
 *
 * @param key
 */
 public void removeCacheFromDisk(String key) {
 try {
  mDiskCache.remove(key);
 } catch (Exception e) {
 }
 }

5.)디스크 캐 시 비우 기 

 /**
 *       
 */
 public void cleanDiskCache() {
 try {
  mDiskCache.delete();
 } catch (Exception e) {
 }
 }

그림 다운로드 과정:
다음 실례 에서 RxJava 지식 을 조금 사 용 했 습 니 다.RxJava 를 모 르 는 사람 은 스스로 알 아 보 세 요. 
1.)디스크 캐 시 와 네트워크 다운 로드 를 비동기 방식 으로 조작 하고 메모리 캐 시 는 메 인 스 레 드 에서 조작 할 수 있 습 니 다. 

 public void disPlay(final ImageView imageView, String imageUrl) {
  //    key
  final String key = DiskLruUtils.hashKeyForDisk(imageUrl);
  //       
  Drawable drawableFromMemCache = getDrawableFromMemCache(key);
  if (drawableFromMemCache != null) {
   imageView.setImageDrawable(drawableFromMemCache);
   return;
  }
  Observable.just(imageUrl)
    .map(new Func1<String, Drawable>() {
     @Override
     public Drawable call(String imageUrl) { //      String
      //      
      Drawable drawableFromDiskCache = getDrawableFromDiskCache(key);
      if (drawableFromDiskCache != null) {
       return drawableFromDiskCache;
      }
      //    
      return download(imageUrl); //      Drawable
     }
    })
    .subscribeOn(Schedulers.io()) //    subscribe()     IO   
    .observeOn(AndroidSchedulers.mainThread()) //    Subscriber          
    .subscribe(new Action1<Drawable>() {
     @Override
     public void call(Drawable drawable) { //      Drawable
      imageView.setImageDrawable(drawable);
     }
    });
 }
2.)이미지 다운로드 과정 및 처리 

 private Drawable download(String imageUrl) {
  HttpURLConnection urlConnection = null;
  ByteArrayOutputStream bos = null;
  InputStream ins = null;
  try {
   final URL url = new URL(imageUrl);
   urlConnection = (HttpURLConnection) url.openConnection();
   ins = urlConnection.getInputStream();
   bos = new ByteArrayOutputStream();
   int b;
   while ((b = ins.read()) != -1) {
    bos.write(b);
   }
   bos.flush();
   byte[] bytes = bos.toByteArray();
   Bitmap bitmap = DiskLruUtils.bytes2Bitmap(bytes);
   String key = DiskLruUtils.hashKeyForDisk(imageUrl);
   Drawable drawable = DiskLruUtils.bitmap2Drawable(bitmap);
   //      
   addDrawableToMemoryCache(key, drawable);
   //      
   addBitmapToDiskCache(key, bytes);
   return drawable;
  } catch (IOException e) {
   e.printStackTrace();
  } finally {
   if (urlConnection != null) {
    urlConnection.disconnect();
   }
   DiskLruUtils.closeQuietly(bos);
   DiskLruUtils.closeQuietly(ins);
  }
  return null;
 }

최종 이미지 캐 시 사례 를 첨부 하여 모든 코드 와 DiskLruUtils 도구 류 코드 를 간단하게 구현 합 니 다.
 ImageLoadManager.java

public class ImageLoadManager {
 private LruCache<String, Drawable> mMemoryCache;//    
 private DiskLruCache mDiskCache;//    
 private static ImageLoadManager mInstance;//          

 /**
  *    
  *
  * @param context
  */
 private ImageLoadManager(Context context) {
  int maxMemory = (int) Runtime.getRuntime().maxMemory();//               
  int mCacheSize = maxMemory / 8;//              
  mMemoryCache = new LruCache<String, Drawable>(mCacheSize) {
   //       ,   Bitmap   
   @Override
   protected int sizeOf(String key, Drawable value) {
    if (value instanceof BitmapDrawable) {
     Bitmap bitmap = ((BitmapDrawable) value).getBitmap();
     return bitmap == null ? 0 : bitmap.getByteCount();
    }
    return super.sizeOf(key, value);
   }
  };

  File cacheDir = context.getCacheDir();//           
  long diskCacheSize = 1024 * 1024 * 30;//             
  int appVersion = DiskLruUtils.getAppVersion(context);//            
  int valueCount = 1;//     key           
  try {
   mDiskCache = DiskLruCache.open(cacheDir, appVersion, valueCount, diskCacheSize);
  } catch (Exception ex) {
  }
 }

 /**
  *       
  *
  * @return
  */
 public static ImageLoadManager getInstance(Context context) {
  ImageLoadManager inst = mInstance;
  if (inst == null) {
   synchronized (RequestManager.class) {
    inst = mInstance;
    if (inst == null) {
     inst = new ImageLoadManager(context.getApplicationContext());
     mInstance = inst;
    }
   }
  }
  return inst;
 }

 public void disPlay(final ImageView imageView, String imageUrl) {
  //    key
  final String key = DiskLruUtils.hashKeyForDisk(imageUrl);
  //       
  Drawable drawableFromMemCache = getDrawableFromMemCache(key);
  if (drawableFromMemCache != null) {
   imageView.setImageDrawable(drawableFromMemCache);
   return;
  }
  Observable.just(imageUrl)
    .map(new Func1<String, Drawable>() {
     @Override
     public Drawable call(String imageUrl) { //      String
      //      
      Drawable drawableFromDiskCache = getDrawableFromDiskCache(key);
      if (drawableFromDiskCache != null) {
       return drawableFromDiskCache;
      }
      //    
      return download(imageUrl); //      Drawable
     }
    })
    .subscribeOn(Schedulers.io()) //    subscribe()     IO   
    .observeOn(AndroidSchedulers.mainThread()) //    Subscriber          
    .subscribe(new Action1<Drawable>() {
     @Override
     public void call(Drawable drawable) { //      Drawable
      imageView.setImageDrawable(drawable);
     }
    });
 }


 /**
  *   Drawable     
  *
  * @param key
  * @param drawable
  */
 private void addDrawableToMemoryCache(String key, Drawable drawable) {
  if (getDrawableFromMemCache(key) == null && drawable != null) {
   mMemoryCache.put(key, drawable);
  }
 }

 /**
  *           Drawable
  *
  * @param key
  * @return
  */
 public Drawable getDrawableFromMemCache(String key) {
  return mMemoryCache.get(key);
 }

 /**
  *           Drawable
  *
  * @param key
  * @return
  */
 public Drawable getDrawableFromDiskCache(String key) {
  try {
   DiskLruCache.Snapshot snapShot = mDiskCache.get(key);
   if (snapShot != null) {
    InputStream is = snapShot.getInputStream(0);
    Bitmap bitmap = BitmapFactory.decodeStream(is);
    Drawable drawable = DiskLruUtils.bitmap2Drawable(bitmap);
    //                
    addDrawableToMemoryCache(key, drawable);
    return drawable;
   }
  } catch (IOException e) {
   e.printStackTrace();
  }
  return null;
 }

 /**
  *   Bitmap     
  *
  * @param key
  * @param value
  */
 private void addBitmapToDiskCache(String key, byte[] value) {
  OutputStream out = null;
  try {
   DiskLruCache.Editor editor = mDiskCache.edit(key);
   if (editor != null) {
    out = editor.newOutputStream(0);
    if (value != null && value.length > 0) {
     out.write(value);
     out.flush();
     editor.commit();
    } else {
     editor.abort();
    }
   }
   mDiskCache.flush();
  } catch (IOException e) {
   e.printStackTrace();
  } finally {
   DiskLruUtils.closeQuietly(out);
  }
 }

 private Drawable download(String imageUrl) {
  HttpURLConnection urlConnection = null;
  ByteArrayOutputStream bos = null;
  InputStream ins = null;
  try {
   final URL url = new URL(imageUrl);
   urlConnection = (HttpURLConnection) url.openConnection();
   ins = urlConnection.getInputStream();
   bos = new ByteArrayOutputStream();
   int b;
   while ((b = ins.read()) != -1) {
    bos.write(b);
   }
   bos.flush();
   byte[] bytes = bos.toByteArray();
   Bitmap bitmap = DiskLruUtils.bytes2Bitmap(bytes);
   String key = DiskLruUtils.hashKeyForDisk(imageUrl);
   Drawable drawable = DiskLruUtils.bitmap2Drawable(bitmap);
   //      
   // addDrawableToMemoryCache(key, drawable);
   //      
   addBitmapToDiskCache(key, bytes);
   return drawable;
  } catch (IOException e) {
   e.printStackTrace();
  } finally {
   if (urlConnection != null) {
    urlConnection.disconnect();
   }
   DiskLruUtils.closeQuietly(bos);
   DiskLruUtils.closeQuietly(ins);
  }
  return null;
 }

 /**
  *       
  *
  * @param key
  */
 public void removeCache(String key) {
  removeCacheFromMemory(key);
  removeCacheFromDisk(key);
 }

 /**
  *         
  *
  * @param key
  */
 public void removeCacheFromMemory(String key) {
  mMemoryCache.remove(key);
 }

 /**
  *         
  *
  * @param key
  */
 public void removeCacheFromDisk(String key) {
  try {
   mDiskCache.remove(key);
  } catch (Exception e) {
  }
 }

 /**
  *       
  *
  * @return
  */
 public long diskCacheSize() {

  return mDiskCache.size();
 }

 /**
  *       
  *
  * @return
  */
 public long memoryCacheSize() {

  return mMemoryCache.size();
 }

 /**
  *       
  */
 public void closeDiskCache() {
  try {
   mDiskCache.close();
  } catch (Exception e) {
  }
 }

 /**
  *     
  */
 public void cleanCache() {
  cleanMemoryCCache();
  cleanDiskCache();
 }

 /**
  *       
  */
 public void cleanDiskCache() {
  try {
   mDiskCache.delete();
  } catch (Exception e) {
  }
 }

 /**
  *       
  */
 public void cleanMemoryCCache() {
  mMemoryCache.evictAll();
 }
}

DiskLruUtils.java

final class DiskLruUtils {

 /**
  *        
  */
 public static void closeQuietly(/*Auto*/Closeable closeable) {
  if (closeable != null) {
   try {
    closeable.close();
   } catch (RuntimeException rethrown) {
    throw rethrown;
   } catch (Exception ignored) {
   }
  }
 }

 /**
  *   versionCode
  */
 public static int getAppVersion(Context context) {
  try {
   PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
   return info.versionCode;
  } catch (PackageManager.NameNotFoundException e) {
   e.printStackTrace();
  }
  return 1;
 }


 public static String hashKeyForDisk(String key) {
  String cacheKey;
  try {
   final MessageDigest mDigest = MessageDigest.getInstance("MD5");
   mDigest.update(key.getBytes());
   cacheKey = bytesToHexString(mDigest.digest());
  } catch (NoSuchAlgorithmException e) {
   cacheKey = String.valueOf(key.hashCode());
  }
  return cacheKey;
 }

 public static String bytesToHexString(byte[] bytes) {
  StringBuilder sb = new StringBuilder();
  for (int i = 0; i < bytes.length; i++) {
   String hex = Integer.toHexString(0xFF & bytes[i]);
   if (hex.length() == 1) {
    sb.append('0');
   }
   sb.append(hex);
  }
  return sb.toString();
 }

 /**
  * Bitmap → bytes
  */
 public static byte[] bitmap2Bytes(Bitmap bm) {
  if (bm == null) {
   return null;
  }
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
  return baos.toByteArray();
 }

 /**
  * bytes → Bitmap
  */
 public static Bitmap bytes2Bitmap(byte[] bytes) {
  return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
 }


 /**
  * Drawable → Bitmap
  */
 public static Bitmap drawable2Bitmap(Drawable drawable) {
  if (drawable == null) {
   return null;
  }
  //   drawable    
  int w = drawable.getIntrinsicWidth();
  int h = drawable.getIntrinsicHeight();
  //   drawable      
  Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
  //      bitmap
  Bitmap bitmap = Bitmap.createBitmap(w, h, config);
  //      bitmap    
  Canvas canvas = new Canvas(bitmap);
  drawable.setBounds(0, 0, w, h);
  //   drawable        
  drawable.draw(canvas);
  return bitmap;
 }

 /*
   * Bitmap → Drawable
   */
 public static Drawable bitmap2Drawable(Bitmap bm) {
  if (bm == null) {
   return null;
  }
  BitmapDrawable bd = new BitmapDrawable(bm);
  bd.setTargetDensity(bm.getDensity());
  return new BitmapDrawable(bm);
 }

}

 이상 은 LRu 이미지 캐 시 를 기반 으로 간단하게 이 루어 졌 습 니 다.여러분 의 학습 에 도움 이 되 고 저희 도 많이 응원 해 주시 기 바 랍 니 다.

좋은 웹페이지 즐겨찾기