这里有新鲜出炉的精品教程,程序狗速度看过来!
Android是一种基于Linux的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由Google公司和开放手机联盟领导及开发。尚未有统一中文名称,中国大陆地区较多人使用“安卓”或“安致”。
这篇文章主要为大家详细介绍了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;
- /**
- * 图片加载类
- *
- * @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, 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;
- }
- }
- /**
- * 显示图片
- *
- * @param imageView
- * @param url
- * @param resId
- */
- 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));
- }
- /**
- * 显示图片固定大小图片的缩略图,一般用于显示列表的图片,可以大大减小内存使用
- *
- * @param imageView 加载图片的控件
- * @param url 加载地址
- * @param resId 默认图片
- * @param width 指定宽度
- * @param height 指定高度
- */
- 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));
- }
- /**
- * 入队,后进先出
- *
- * @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, 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;
- }
- }
- }
- /** 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, 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);
- }
- }
- /**
- * 从网络获取图片字节数组
- *
- * @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();
- }
- }
来源: http://www.phperz.com/article/17/0914/348988.html