利用Java Soft Reference技术实现Android图片管理器

最近做一个项目,需要用到图片管理器,对所有的图片进行全局存储和读取。
目标比较明确:在不抛出OOM异常的情况下,尽量加速图片的读取速度。众所周知,手机这种资源受限型设备,对内存的使用还是需要精打细算的。
本人接触Java时间不长,3个月多一点,对Java的内存管理机制理解的不透彻,所以刚开始走了一些弯路,最后才找到了Soft Reference这个东西。看来,以后必须要学习一下
Java的内存管理啦(此时不得不怀念C的自由和强大)。
言归正传,还是说说这个图片管理器。项目中图片分2种类型,一种是系统资源图片,本质上是编译成Android R的二进制文件;另一种是用户自定义的图片,大多数以png、
bitmap的格式存储在本地或者服务器端。所以在管理器中设计了2张HashMap存放这两类图片。本来想把图片设计成Bitmap的,后来发现设计成Drawable更加方便。
下面展示一下代码:由于是公司内部的项目,所以代码不能全部贴出来。要保持最起码的职业道德嘛。
/**
* This class is a global image manager class.
* It has three maps:
* systemImageMap_:which holds the image for system usage;
* customImageMap_:which holds the image for app-related/custom usage.
* In order to make memory more efficient,they are used through SoftReference.
*/
public class ImageManager
{
private static ImageManager gInstance_;
private static Resources resourcesMan = R.getResources();
/* storage for system image cache */
private HashMap systemImageRefs;
/* queue for garbage system image reference */
private ReferenceQueue systemImageRefQueue;
/* storage for custom image cache */
private HashMap customImageRefs;
/* queue for garbage custom image reference */
private ReferenceQueue customImageRefQueue;
public static final String log_tag = "ImageManager ";
private ImageManager()
{
systemImageRefs = new HashMap();
systemImageRefQueue = new ReferenceQueue();
customImageRefs = new HashMap();
customImageRefQueue = new ReferenceQueue();
}
public static ImageManager getInstance()
{
if (gInstance_ == null)
{
gInstance_ = new ImageManager();
}
return gInstance_;
}
/**
* Get system drawable according to the enumeration value of system image.
*
* @param imageID
* : enumeration value of system image
* @return null if there is no such image id otherwise a system drawable
* image
*/
public Drawable getSystemImage(int resourceID)
{
/* If it's already cached through soft reference, get it directly */
SystemImage sysImage = null;
if (systemImageRefs.containsKey(resourceID))
{
SystemImageRef ref = systemImageRefs.get(resourceID);
sysImage = ref.get();
}
/*
* If this soft reference does not exist, or the entity is null, rebuild
* a new entity and save the new soft reference
*/
if (null == sysImage)
{
sysImage = new SystemImage(resourceID);
cacheSystemImage(sysImage);
Log.i(log_tag, "Retrieve image from R, resourceID = " + resourceID);
}
return sysImage.getSystemImage();
}
/**
* clean SystemImageRef entities whose SystemImage has been recycled by VM
* already
*/
private void cleanSystemImageCache()
{
SystemImageRef ref = null;
while ((ref = (SystemImageRef) systemImageRefQueue.poll()) != null)
{
systemImageRefs.remove(ref.resourceID);
}
return;
}
/**
* cache a new system image
*
* @param em
* :entity of system image to be cached
*/
private void cacheSystemImage(SystemImage em)
{
cleanSystemImageCache();
SystemImageRef ref = new SystemImageRef(em, systemImageRefQueue);
systemImageRefs.put(ref.resourceID, ref);
return;
}
/**
* Get custom drawable according to the specified imageURL
*
* @param imageURL
* : URL of image, must not be null or 0 length
* @return null if there is no image at imageURL, otherwise a custom
* drawable image
*/
public Drawable getCustomImage(String imageURL)
{
if (null == imageURL || 0 == imageURL.length())
{
return null;
}
/* If it's already cached through soft reference, get it directly */
CustomImage cusImage = null;
if (customImageRefs.containsKey(imageURL))
{
CustomImageRef ref = customImageRefs.get(imageURL);
cusImage = ref.get();
}
/*
* If this soft reference does not exist, or the entity is null, rebuild
* a new entity and save the new soft reference
*/
if (null == cusImage)
{
cusImage = new CustomImage(imageURL);
cacheCustomImage(cusImage);
Log.i(log_tag, "Retrieve image imageURL = " + imageURL);
}
return cusImage.getCustomImage();
}
/**
* cache a new custom image
*
* @param em
* :entity of custom image to be cached
*/
private void cacheCustomImage(CustomImage em)
{
cleanCustomImageCache();
CustomImageRef ref = new CustomImageRef(em, customImageRefQueue);
customImageRefs.put(ref.imageURL, ref);
return;
}
/**
* clean CustomImageRef entities whose CustomImage has been recycled by VM
* already
*/
private void cleanCustomImageCache()
{
CustomImageRef ref = null;
while ((ref = (CustomImageRef) customImageRefQueue.poll()) != null)
{
customImageRefs.remove(ref.imageURL);
}
return;
}
/**
* clear all the cache
*/
public void clearCache()
{
cleanSystemImageCache();
cleanCustomImageCache();
systemImageRefs.clear();
customImageRefs.clear();
System.gc();
//System.runFinalization();
return;
}
/**
* Image for system usage
*/
private class SystemImage
{
private int resourceID;
private Drawable systemDrawable;
public SystemImage(int resourceID)
{
this.resourceID = resourceID;
this.systemDrawable = resourcesMan.getDrawable(resourceID);
}
public int getResourceID()
{
return this.resourceID;
}
public Drawable getSystemImage()
{
return this.systemDrawable;
}
}
/**
* Image for Custom usage
*/
private class CustomImage
{
private String imageURL;
private Drawable customDrawable;
public CustomImage(String imageURL)
{
this.imageURL = new String(imageURL);
this.customDrawable = FileUtils.getDrawable(imageURL);
}
public String getImagePath()
{
return this.imageURL;
}
public Drawable getCustomImage()
{
return this.customDrawable;
}
}
/**
* Extends SoftReference, make every entity has a distinguishable resourceID
* and this resourceID is identical with the key for systemImageRefs
*
*/
private class SystemImageRef extends SoftReference
{
private int resourceID;
public SystemImageRef(SystemImage em, ReferenceQueue q)
{
super(em, q);
resourceID = em.getResourceID();
}
}
/**
* Extends SoftReference, make every entity has a distinguishable imageURL
* and this imageURL is identical with the key for CustomImageRefs
*
*/
private class CustomImageRef extends SoftReference
{
private String imageURL;
public CustomImageRef(CustomImage em, ReferenceQueue q)
{
super(em, q);
imageURL = em.getImagePath();
}
}
}
目前为止,这段代码工作正常,通过打出的日志,可以看见,VM在某些时候的确释放了一些已经缓存在HashMap中的图片,目的大概就是书上说的避免抛出OOM异常吧。但是内存
的实际使用效率如何,所谓的Soft Reference到底如何平衡时间和空间的,没有直接的说明数据。我设计了一种实验方法,评估这段代码的效率,用一个XML脚本不停地随机读取
图片资源,看看手机的实际反应情况。很不幸,貌似手机反应有点慢,现在郁闷中。另外对于存储在服务器端的图片资源,目前的代码没有考虑到这一点,或许要开一个子线程去网
络上下载图片,那是后话了,目前还有一些人在狂写服务端的代码呢。
根据使用中出现的问题,我会持续更新这段代码的,以期完美实现最初的目标:既省内存又省时间。
另外,希望某位大侠给我介绍一些Java内存管理机制的知识,或者一起探讨一下也行。
Tags: 

延伸阅读

最新评论

发表评论