Android实现图片缓存与异步加载

Android实现图片缓存与异步加载,第1张

概述ImageManager2这个类具有异步从网络下载图片,从sd读取本地图片,内存缓存,硬盘缓存,图片使用动画渐现等功能,已经将其应用在包含大量图片的应用中一年多,没有出现oom。

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实现图片缓存与异步加载所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1142002.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存