ImageManager2这个类具有异步从网络下载图片,从sd读取本地图片,内存缓存,硬盘缓存,图片使用动画渐现等功能,已经将其应用在包含大量图片的应用中一年多,没有出现oom。
AndroID程序常常会内存溢出,网上也有很多解决方案,如软引用,手动调用recycle等等。但经过我们实践发现这些方案,都没能起到很好的效果,我们的应用依然会出现很多oom,尤其我们的应用包含大量的图片。androID3.0之后软引用基本已经失效,因为虚拟机只要碰到软引用就回收,所以带不来任何性能的提升。
我这里的解决方案是HandlerThread(异步加载)+LruCache(内存缓存)+diskLruCache(硬盘缓存)。
作为程序员,我也不多说,直接和大家共享我的代码,用代码交流更方便些。
package com.example.util; import java.io.file;import java.util.Iterator;import java.util.linkedList;import java.util.Queue;import java.util.Stack; import org.apache.http.httpentity;import org.apache.http.httpResponse;import org.apache.http.clIEnt.methods.httpGet;import org.apache.http.util.EntityUtils; import androID.app.ActivityManager;import androID.content.Context;import androID.graphics.Bitmap;import androID.graphics.BitmapFactory;import androID.graphics.drawable.BitmapDrawable;import androID.graphics.drawable.colorDrawable;import androID.graphics.drawable.Drawable;import androID.graphics.drawable.TransitionDrawable;import androID.media.thumbnailUtils;import androID.os.Handler;import androID.os.HandlerThread;import androID.os.Looper;import androID.os.Message;import androID.support.v4.util.LruCache;import androID.Widget.ImageVIEw; import com.example.MyApplication; /** * 图片加载类 * * @author 月月鸟 */public class ImageManager2 { private static ImageManager2 imageManager; public LruCache<String,Bitmap> mMemoryCache; private static final int disK_CACHE_SIZE = 1024 * 1024 * 20; // 10MB private static final String disK_CACHE_SUBDIR = "thumbnails"; public diskLruCache mdiskCache; private static MyApplication myapp; /** 图片加载队列,后进先出 */ private Stack<ImageRef> mImageQueue = new Stack<ImageRef>(); /** 图片请求队列,先进先出,用于存放已发送的请求。 */ private Queue<ImageRef> mRequestQueue = new linkedList<ImageRef>(); /** 图片加载线程消息处理器 */ private Handler mImageLoaderHandler; /** 图片加载线程是否就绪 */ private boolean mImageLoaderIDle = true; /** 请求图片 */ private static final int MSG_REQUEST = 1; /** 图片加载完成 */ private static final int MSG_REPLY = 2; /** 中止图片加载线程 */ private static final int MSG_Stop = 3; /** 如果图片是从网络加载,则应用渐显动画,如果从缓存读出则不应用动画 */ private boolean isFromNet = true; /** * 获取单例,只能在UI线程中使用。 * * @param context * @return */ public static ImageManager2 from(Context context) { // 如果不在ui线程中,则抛出异常 if (Looper.myLooper() != Looper.getMainLooper()) { throw new RuntimeException("Cannot instantiate outsIDe UI thread."); } if (myapp == null) { myapp = (MyApplication) context.getApplicationContext(); } if (imageManager == null) { imageManager = new ImageManager2(myapp); } return imageManager; } /** * 私有构造函数,保证单例模式 * * @param context */ private ImageManager2(Context context) { int memClass = ((ActivityManager) context .getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass(); memClass = memClass > 32 ? 32 : memClass; // 使用可用内存的1/8作为图片缓存 final int cacheSize = 1024 * 1024 * memClass / 8; mMemoryCache = new LruCache<String,Bitmap>(cacheSize) { protected int sizeOf(String key,Bitmap bitmap) { return bitmap.getRowBytes() * bitmap.getHeight(); } }; file cacheDir = diskLruCache .getdiskCacheDir(context,disK_CACHE_SUBDIR); mdiskCache = diskLruCache.openCache(context,cacheDir,disK_CACHE_SIZE); } /** * 存放图片信息 */ class ImageRef { /** 图片对应ImageVIEw控件 */ ImageVIEw imageVIEw; /** 图片URL地址 */ String url; /** 图片缓存路径 */ String filePath; /** 默认图资源ID */ int resID; int wIDth = 0; int height = 0; /** * 构造函数 * * @param imageVIEw * @param url * @param resID * @param filePath */ ImageRef(ImageVIEw imageVIEw,String url,String filePath,int resID) { this.imageVIEw = imageVIEw; this.url = url; this.filePath = filePath; this.resID = resID; } ImageRef(ImageVIEw imageVIEw,int resID,int wIDth,int height) { this.imageVIEw = imageVIEw; this.url = url; this.filePath = filePath; this.resID = resID; this.wIDth = wIDth; this.height = height; } } /** * 显示图片 * * @param imageVIEw * @param url * @param resID */ public voID displayImage(ImageVIEw imageVIEw,int resID) { if (imageVIEw == null) { return; } if (imageVIEw.getTag() != null && imageVIEw.getTag().toString().equals(url)) { return; } if (resID >= 0) { if (imageVIEw.getBackground() == null) { imageVIEw.setBackgroundResource(resID); } imageVIEw.setimageDrawable(null); } if (url == null || url.equals("")) { return; } // 添加url tag imageVIEw.setTag(url); // 读取map缓存 Bitmap bitmap = mMemoryCache.get(url); if (bitmap != null) { setimageBitmap(imageVIEw,bitmap,false); return; } // 生成文件名 String filePath = urlTofilePath(url); if (filePath == null) { return; } queueImage(new ImageRef(imageVIEw,url,filePath,resID)); } /** * 显示图片固定大小图片的缩略图,一般用于显示列表的图片,可以大大减小内存使用 * * @param imageVIEw 加载图片的控件 * @param url 加载地址 * @param resID 默认图片 * @param wIDth 指定宽度 * @param height 指定高度 */ public voID displayImage(ImageVIEw imageVIEw,int height) { if (imageVIEw == null) { return; } if (resID >= 0) { if (imageVIEw.getBackground() == null) { imageVIEw.setBackgroundResource(resID); } imageVIEw.setimageDrawable(null); } if (url == null || url.equals("")) { return; } // 添加url tag imageVIEw.setTag(url); // 读取map缓存 Bitmap bitmap = mMemoryCache.get(url + wIDth + height); if (bitmap != null) { setimageBitmap(imageVIEw,resID,wIDth,height)); } /** * 入队,后进先出 * * @param imageRef */ public voID queueImage(ImageRef imageRef) { // 删除已有ImageVIEw Iterator<ImageRef> iterator = mImageQueue.iterator(); while (iterator.hasNext()) { if (iterator.next().imageVIEw == imageRef.imageVIEw) { iterator.remove(); } } // 添加请求 mImageQueue.push(imageRef); sendRequest(); } /** * 发送请求 */ private voID sendRequest() { // 开启图片加载线程 if (mImageLoaderHandler == null) { HandlerThread imageLoader = new HandlerThread("image_loader"); imageLoader.start(); mImageLoaderHandler = new ImageLoaderHandler( imageLoader.getLooper()); } // 发送请求 if (mImageLoaderIDle && mImageQueue.size() > 0) { ImageRef imageRef = mImageQueue.pop(); Message message = mImageLoaderHandler.obtainMessage(MSG_REQUEST,imageRef); mImageLoaderHandler.sendMessage(message); mImageLoaderIDle = false; mRequestQueue.add(imageRef); } } /** * 图片加载线程 */ class ImageLoaderHandler extends Handler { public ImageLoaderHandler(Looper looper) { super(looper); } public voID handleMessage(Message msg) { if (msg == null) return; switch (msg.what) { case MSG_REQUEST: // 收到请求 Bitmap bitmap = null; Bitmap tBitmap = null; if (msg.obj != null && msg.obj instanceof ImageRef) { ImageRef imageRef = (ImageRef) msg.obj; String url = imageRef.url; if (url == null) return; // 如果本地url即读取sd相册图片,则直接读取,不用经过diskCache if (url.tolowerCase().contains("dcim")) { tBitmap = null; BitmapFactory.Options opt = new BitmapFactory.Options(); opt.inSampleSize = 1; opt.inJustDecodeBounds = true; BitmapFactory.decodefile(url,opt); int bitmapSize = opt.outHeight * opt.outWIDth * 4; opt.inSampleSize = bitmapSize / (1000 * 2000); opt.inJustDecodeBounds = false; tBitmap = BitmapFactory.decodefile(url,opt); if (imageRef.wIDth != 0 && imageRef.height != 0) { bitmap = thumbnailUtils.extractthumbnail(tBitmap,imageRef.wIDth,imageRef.height,thumbnailUtils.OPTIONS_RECYCLE_input); isFromNet = true; } else { bitmap = tBitmap; tBitmap = null; } } else bitmap = mdiskCache.get(url); if (bitmap != null) { // ToolUtil.log("从disk缓存读取"); // 写入map缓存 if (imageRef.wIDth != 0 && imageRef.height != 0) { if (mMemoryCache.get(url + imageRef.wIDth + imageRef.height) == null) mMemoryCache.put(url + imageRef.wIDth + imageRef.height,bitmap); } else { if (mMemoryCache.get(url) == null) mMemoryCache.put(url,bitmap); } } else { try { byte[] data = loadByteArrayFromNetwork(url); if (data != null) { BitmapFactory.Options opt = new BitmapFactory.Options(); opt.inSampleSize = 1; opt.inJustDecodeBounds = true; BitmapFactory.decodeByteArray(data,data.length,opt); int bitmapSize = opt.outHeight * opt.outWIDth * 4;// pixels*3 if it's RGB and pixels*4 // if it's ARGB if (bitmapSize > 1000 * 1200) opt.inSampleSize = 2; opt.inJustDecodeBounds = false; tBitmap = BitmapFactory.decodeByteArray(data,opt); if (imageRef.wIDth != 0 && imageRef.height != 0) { bitmap = thumbnailUtils .extractthumbnail( tBitmap,thumbnailUtils.OPTIONS_RECYCLE_input); } else { bitmap = tBitmap; tBitmap = null; } if (bitmap != null && url != null) { // 写入SD卡 if (imageRef.wIDth != 0 && imageRef.height != 0) { mdiskCache.put(url + imageRef.wIDth + imageRef.height,bitmap); mMemoryCache.put(url + imageRef.wIDth + imageRef.height,bitmap); } else { mdiskCache.put(url,bitmap); mMemoryCache.put(url,bitmap); } isFromNet = true; } } } catch (OutOfMemoryError e) { } } } if (mImageManagerHandler != null) { Message message = mImageManagerHandler.obtainMessage( MSG_REPLY,bitmap); mImageManagerHandler.sendMessage(message); } break; case MSG_Stop: // 收到终止指令 Looper.myLooper().quit(); break; } } } /** UI线程消息处理器 */ private Handler mImageManagerHandler = new Handler() { @OverrIDe public voID handleMessage(Message msg) { if (msg != null) { switch (msg.what) { case MSG_REPLY: // 收到应答 do { ImageRef imageRef = mRequestQueue.remove(); if (imageRef == null) break; if (imageRef.imageVIEw == null || imageRef.imageVIEw.getTag() == null || imageRef.url == null) break; if (!(msg.obj instanceof Bitmap) || msg.obj == null) { break; } Bitmap bitmap = (Bitmap) msg.obj; // 非同一ImageVIEw if (!(imageRef.url).equals((String) imageRef.imageVIEw .getTag())) { break; } setimageBitmap(imageRef.imageVIEw,isFromNet); isFromNet = false; } while (false); break; } } // 设置闲置标志 mImageLoaderIDle = true; // 若服务未关闭,则发送下一个请求。 if (mImageLoaderHandler != null) { sendRequest(); } } }; /** * 添加图片显示渐现动画 * */ private voID setimageBitmap(ImageVIEw imageVIEw,Bitmap bitmap,boolean isTran) { if (isTran) { final TransitionDrawable td = new TransitionDrawable( new Drawable[] { new colorDrawable(androID.R.color.transparent),new BitmapDrawable(bitmap) }); td.setCrossFadeEnabled(true); imageVIEw.setimageDrawable(td); td.startTransition(300); } else { imageVIEw.setimageBitmap(bitmap); } } /** * 从网络获取图片字节数组 * * @param url * @return */ private byte[] loadByteArrayFromNetwork(String url) { try { httpGet method = new httpGet(url); httpResponse response = myapp.gethttpClIEnt().execute(method); httpentity entity = response.getEntity(); return EntityUtils.toByteArray(entity); } catch (Exception e) { return null; } } /** * 根据url生成缓存文件完整路径名 * * @param url * @return */ public String urlTofilePath(String url) { // 扩展名位置 int index = url.lastIndexOf('.'); if (index == -1) { return null; } StringBuilder filePath = new StringBuilder(); // 图片存取路径 filePath.append(myapp.getCacheDir().toString()).append('/'); // 图片文件名 filePath.append(MD5.Md5(url)).append(url.substring(index)); return filePath.toString(); } /** * Activity#onStop后,ListVIEw不会有残余请求。 */ public voID stop() { // 清空请求队列 mImageQueue.clear(); } }
这里就是给出了异步加载、内存缓存和硬盘缓存的解决方案,希望对大家的学习有所帮助。
总结以上是内存溢出为你收集整理的Android实现图片缓存与异步加载全部内容,希望文章能够帮你解决Android实现图片缓存与异步加载所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)