Android动画异步加载,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 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;

/**

* 获取单例,只能在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(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 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();

}

}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值