栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 移动开发 > Android

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

Android 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

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;
 

public class ImageManager2 {
 
  private static ImageManager2 imageManager;
  public LruCache 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 mImageQueue = new Stack();
 
  
  private Queue mRequestQueue = new linkedList();
 
  
  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;
 
  
  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;
  }
 
  
  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(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;
    
    String url;
    
    String filePath;
    
    int resId;
    int width = 0;
    int height = 0;
 
    
    ImageRef(ImageView imageView, String url, String filePath, int resId) {
      this.imageView = imageView;
      this.url = url;
      this.filePath = filePath;
      this.resId = resId;
    }
 
    ImageRef(ImageView imageView, String url, String filePath, int resId,
 int width, int height) {
      this.imageView = imageView;
      this.url = url;
      this.filePath = filePath;
      this.resId = resId;
      this.width = width;
      this.height = height;
    }
 
  }
 
  
  public void displayImage(ImageView imageView, String url, 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));
  }
 
  
  public void displayImage(ImageView imageView, String url, int resId,
      int width, 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, bitmap, false);
      return;
    }
 
    // 生成文件名
    String filePath = urlToFilePath(url);
    if (filePath == null) {
      return;
    }
 
    queueImage(new ImageRef(imageView, url, filePath, resId, width, height));
  }
 
  
  public void queueImage(ImageRef imageRef) {
 
    // 删除已有ImageView
    Iterator 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, 0,
      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,
      0, data.length, opt);
  if (imageRef.width != 0 && imageRef.height != 0) {
    bitmap = ThumbnailUtils
 .extractThumbnail(
     tBitmap,
     imageRef.width,
     imageRef.height,
     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;
 
      }
    }
  }
 
  
  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, bitmap, 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);
    }
  }
 
  
  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;
    }
 
  }
 
  
  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();
  }
 
  
  public void stop() {
 
    // 清空请求队列
    mImageQueue.clear();
 
  }
 
}

这里就是给出了异步加载、内存缓存和硬盘缓存的解决方案,希望对大家的学习有所帮助。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/163218.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号