날짜별 글 목록: 2013년 7월 24일

안드로이드(Android) 앱에서 메모리 캐시 사이즈를 디바이스별로 동적으로 조절하기

안드로이드(Android)에서 많은 썸네일(Thumbnail)을 처리하기 위해서 많은 앱들이 1차 메모리, 2차 디스크 캐시를 사용하고 있다. 여기에서 2차 디스크는 용량이 충분하기 때문에 별 이슈가 없다. 하지만, 메모리 캐시의 경우에는 디바이스별로 다르기 때문에 보통은 작은 사이즈로 지정해서 사용하게 된다. 지금 출시되는 안드로이드 디바이스의 앱 메모리 설정을 보면, 이전보다 매우 크게 사용할 수 있는 것을 알수가 있다.

그래서, 메모리 캐시를 더 크게 사용해서, UX의 반응을 더 높이는 방법을 살펴보자. 기본적으로, http://www.sjava.net/332 를 보면,  안드로이드 OS의 설정정보를 읽어서, 디바이스의 정보를 알수가 있겠다.

앱마다 사용하는 썸네일 사이즈에 따라, 메모리를 점유하는 크기가 달라지기 때문에 이 내용은 앱에서 처리하는 썸네일의 크기에 따라 달라진다.

우선, 썸네일이 점유하는 메모리의 크기를 살펴보면..

100 x 100의 썸네일인 경우, 100x100x4의 크기이니 40,000 byte의 메모리를 점유한다고 보면 되겠다.  그래서, 대략적으로 캐시 사이즈에 따른 메모리 사용량을 가늠해 보면..

100×100 썸네일을 메모리에 100개 올리게 되면, 4M 정도의 메모리를 점유하게 될 것이고.. 이 정보를 기준으로 캐시 사이즈를 정하면 되겠다..

위의, 정보를 기준으로 해서 디바이스가 허용하는 앱의 힙 정보를 살펴보면..

* 갤럭시 넥서스

07-24 11:51:04.377: E/CacheUtil(27521): dalvik.vm.heapstartsize : 8m
07-24 11:51:04.377: E/CacheUtil(27521): dalvik.vm.heapgrowthlimit : 96m <– 최대 힙 사이즈를 기준으로 캐시의 사이즈를 조절한다.
07-24 11:51:04.377: E/CacheUtil(27521): dalvik.vm.heapsize : 256m <– 전체 힙 사이즈

* 넥서스 10

07-24 12:02:46.003: E/CacheUtil(12619): dalvik.vm.heapstartsize : 16m
07-24 12:02:46.008: E/CacheUtil(12619): dalvik.vm.heapgrowthlimit : 192m <– 최대 힙 사이즈를 기준으로 캐시의 사이즈를 조절한다.
07-24 12:02:46.008: E/CacheUtil(12619): dalvik.vm.heapsize : 512m <– 전체 힙 사이즈

위의 정보를 바탕으로 캐시클래스를 만들어 보면..

import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.graphics.Bitmap;

public class CacheUtil {
	static final String TAG = CacheUtil.class.getSimpleName();
	static int THUMBNAIL_CACHE_COUNT = 100;
	static boolean DEBUG = true;

	static{
		if(DEBUG) {
			Logger.e(TAG, "dalvik.vm.heapstartsize : "  + get("dalvik.vm.heapstartsize"));
			Logger.e(TAG, "dalvik.vm.heapgrowthlimit : "  + get("dalvik.vm.heapgrowthlimit"));
			Logger.e(TAG, "dalvik.vm.heapsize : "  + get("dalvik.vm.heapsize"));
			Logger.e(TAG, "cache_size : "  + getDynamicCacheSize());
		}
		
		THUMBNAIL_CACHE_COUNT = getDynamicCacheSize();
	}
		
	static LruCache<String, Bitmap> listCache = new LruCache<String, Bitmap>(THUMBNAIL_CACHE_COUNT);
	public static LruCache<String, Bitmap> listCache() {
		if(listCache == null)
			listCache = new LruCache<String, Bitmap>(THUMBNAIL_CACHE_COUNT);
		
		return listCache;
	}
	
	static LruCache<String, Bitmap> gridCache = new LruCache<String, Bitmap>(THUMBNAIL_CACHE_COUNT);
	public static LruCache<String, Bitmap> gridCache() {
		if(gridCache == null)
			gridCache = new LruCache<String, Bitmap>(THUMBNAIL_CACHE_COUNT);
		
		return gridCache;
	}

	
	static int getDynamicCacheSize() {
		String value = get("dalvik.vm.heapgrowthlimit");
		if(StringUtil.isEmpty(value))
			return THUMBNAIL_CACHE_COUNT;
		
		// M가 단위로 가져온다.. 
		int size = extractInt(value);
		if(size <= 64)
			return 100;
		
		if(size <= 128)
			return 200;
		
		return 300;
	}
	
	private static String get(String key) {
		try {
			Class clazz = Class.forName("android.os.SystemProperties");
			if (clazz == null)
				return "";

			Method method = clazz.getDeclaredMethod("get", String.class);
			if (method == null)
				return "";

			return (String) method.invoke(null, key);
		} catch (Exception e) {
			if (DEBUG)
				Logger.e(TAG, ExceptionUtil.exception(e));
		}

		return "";
	}
	
	private static int extractInt(String str) {
        Matcher matcher = Pattern.compile("\\d+").matcher(str);
        if (!matcher.find())
            throw new NumberFormatException("For input string [" + str + "]");

        return Integer.parseInt(matcher.group());
    }
}

위 클래스를 이용해서, 디바이스에 설정되어 있는 메모리 크기의 정보를 기준으로 메모리 캐시의 크기를 동적으로 설정할 수가 있겠다.