您的位置:首页 > 其它

异步图片加载、内存、磁盘缓存

2014-03-05 17:08 369 查看
该类实现的功能:

1. 异步加载远程图片

2. 图片内存缓存

3. 异步图片磁盘缓存

package com.ai9475.util;

import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;

import com.ai9475.meitian.AppConfig;
import com.ai9475.meitian.AppException;
import com.ai9475.meitian.AppManager;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;

/**
* 图片加载管理器
*
* Created by ZHOUZ on 14-2-7.
*/
public final class ZImageLoader
{
private static final String TAG = "ZImageLoader";
private static final ZImageLoader INSTANCE = new ZImageLoader();
private static String mCacheBasePath = null;
private boolean mUseDiskCache = false;
private int mExpireTime = 86400;
private String mCacheSubDir = "common";
private HashMap<String, SoftReference<Drawable>> mImageCache = new HashMap<String, SoftReference<Drawable>>();

public static ZImageLoader getInstance() {
return INSTANCE;
}

/**
* 设置 SD 卡中的图片缓存有效时长(单位:秒)
*
* @param time
*/
public void setExpireTime(int time) {
this.mExpireTime = time;
}

/**
* 设置是否使用存储卡缓存图片
*
* @param isUse
*/
public void setUseDiskCache(Boolean isUse) {
this.mUseDiskCache = isUse;
}

/**
* 设置缓存根目录
*
* @param path
*/
public static void setCacheBasePath(String path) {
mCacheBasePath = path;
}

/**
* 设置缓存根目录下的子目录
*
* @param dir
*/
public void setmCacheSubDir(String dir) {
this.mCacheSubDir = dir;
}

/**
* 加载图片的多线程控制
*
* @param imageUrl
* @param tag
* @param listener
*/
public void loadDrawable(final String imageUrl, final String tag, final OnImageLoadListener listener)
{
// 异步多线程加载图片后的数据传递处理
final Handler handler = new Handler() {
@Override
public void handleMessage(Message message) {
if (message.what == 1) {
listener.onSuccess((Drawable) message.obj, imageUrl, tag);
} else {
listener.onFailure((IOException) message.obj, imageUrl, tag);
}
}
};

// 通过线程池来控制管理图片加载
AppManager.getFixedExecutorsService().submit(new Runnable() {
@Override
public void run() {
Message msg;
try {
Drawable drawable = null;
// 是否已缓存过图片, 是则从缓存中直接获取, 若缓存中数据丢失则重新远程加载
if (mImageCache.containsKey(imageUrl)) {
SoftReference<Drawable> softReference = mImageCache.get(imageUrl);
if (softReference != null) {
Drawable d = softReference.get();
if (d != null) {
ZLog.i(TAG, "load image from memory cache");
drawable = d;
}
}
}
if (drawable == null) {
drawable = loadImageFromUrl(imageUrl);
if (drawable != null) {
mImageCache.remove(imageUrl);
mImageCache.put(imageUrl, new SoftReference<Drawable>(drawable));
}
}
msg = handler.obtainMessage(1, drawable);
} catch (IOException e) {
msg = handler.obtainMessage(0, e);
}
handler.sendMessage(msg);
}
});

}

/**
* 加载远程图片或本地图片缓存文件
*
* @param imageUrl
* @return
* @throws java.io.IOException
*/
public Drawable loadImageFromUrl(String imageUrl) throws IOException
{
// 检查 SD 卡是否可用并将图片缓存到 SD 卡上
if (mUseDiskCache && mCacheBasePath != null)
{
File d = new File(mCacheBasePath + mCacheSubDir);
if (! d.exists()) {
d.mkdirs();
}

final File f = new File(d.getPath() + File.separator + ZHelper.md5(imageUrl));
long time = (new Date()).getTime();
long expire = time - (mExpireTime * 1000L);

// 文件存在且在有效期内则直接读取
if (f.exists() && f.lastModified() > expire) {
ZLog.i(TAG, "load image file disk cache");
FileInputStream fis = new FileInputStream(f);
return Drawable.createFromStream(fis, "src");
}

// 远程加载图片后写入到 SD 卡上
InputStream i = this.getImageInputStream(imageUrl);
if (i == null) {
return null;
}

final Drawable drawable = Drawable.createFromStream(i, "src");
if (drawable == null) {
return null;
}

// 将图片异步写入到本地 SD 卡中缓存, 避免阻塞UI线程, 导致图片不能显示
new Thread(new Runnable() {
@Override
public void run() {
ZLog.i(TAG, "async write image file disk cache");
try {
BitmapDrawable bd = (BitmapDrawable) drawable;
Bitmap bitmap = bd.getBitmap();
FileOutputStream out = new FileOutputStream(f);
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.close();
} catch (IOException e) {
ZLog.e(TAG, "write image cache IOException: "+ e.getMessage());
AppException.io(e);
}
}
}).start();

return drawable;
}
// 只读取远程图片不缓存
else {
InputStream i = this.getImageInputStream(imageUrl);
if (i == null) {
return null;
}
return Drawable.createFromStream(i, "src");
}
}

/**
* 远程加载图片数据
*
* @param imageUrl
* @return
* @throws java.io.IOException
*/
public InputStream getImageInputStream(String imageUrl) throws IOException
{
ZLog.i(TAG, "load image from url");
if (! ZConnectivity.isNetworkConnected()) {
ZLog.i(TAG, "net work not connected");
return null;
}
URL m = new URL(imageUrl);
HttpURLConnection conn = (HttpURLConnection) m.openConnection();
conn.setRequestMethod("GET");
conn.setUseCaches(false);
conn.setDoInput(true);
conn.setConnectTimeout(5000);
conn.setReadTimeout(30000);
conn.setInstanceFollowRedirects(true);
return conn.getInputStream();
}

/**
* 加载图片的事件监听器
*/
public interface OnImageLoadListener {
/**
* 图片加载完成事件处理
*
* @param imageDrawable
* @param imageUrl
* @param tag
*/
public void onSuccess(Drawable imageDrawable, String imageUrl, String tag);

/**
* 图片加载失败的事件处理
*
* @param e
* @param imageUrl
* @param tag
*/
public void onFailure(IOException e, String imageUrl, String tag);
}
}


使用方法:

/**
* 日记照片加载事件监听
*/
private class OnPicLoadListener extends OnImageLoadListener
{
private int mImageSource = R.drawable.default_pic;

/**
* 设置图片
*
* @param view
* @param imageUrl
* @param tag
* @param drawable
*/
public void setDrawable(ImageView view, String imageUrl, String tag, Drawable drawable)
{
if (view == null) return;
int height = 0;
if (mImagesHeight.containsKey(imageUrl)) {
height = mImagesHeight.get(imageUrl);
}
View ct = ((View) view.getParent()).findViewById(R.id.calendarCt);
if (ct != null) {
ct.setVisibility(View.INVISIBLE);
}
if (drawable != null) {
// 定义图片的最佳高度
if (height == 0) {
int minHeight = ZUI.dp2px(mContext, PIC_MIN_HEIGHT);
int maxHeight = ZUI.dp2px(mContext, PIC_MAX_HEIGHT);
height = (int) ((float) view.getWidth() / drawable.getMinimumWidth() * drawable.getMinimumHeight());
if (height > maxHeight) {
height = maxHeight;
} else if (height < minHeight) {
height = minHeight;
}
mImagesHeight.put(imageUrl, height);
}
// 现将图片完全透明
drawable.setAlpha(0);
view.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, height));
view.setScaleType(ImageView.ScaleType.CENTER_CROP);
view.setImageDrawable(drawable);
// 添加透明渐变动画显示图片
AlphaAnimation alphaAnim = new AlphaAnimation(0.0f, 1.0f);
alphaAnim.setDuration(100);
view.setAnimation(alphaAnim);
if (ct != null) {
ct.setVisibility(View.VISIBLE);
}
} else {
int minHeight = ZUI.dp2px(mContext, PIC_MIN_HEIGHT);
height = height < minHeight ? minHeight : height;
view.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, height));
view.setScaleType(ImageView.ScaleType.CENTER);
view.setImageResource(mImageSource);
}
}
}

/**
* 图片的加载监听事件
*/
abstract private class OnImageLoadListener implements ZImageLoader.OnImageLoadListener
{
/**
* 实现图片显示的抽象方法
*
* @param view
* @param tag
* @param drawable
*/
abstract public void setDrawable(ImageView view, String imageUrl, String tag, Drawable drawable);

@Override
public void onSuccess(Drawable drawable, String imageUrl, String tag) {
ImageView view = (ImageView) mDiaryListView.findViewWithTag(tag == null ? imageUrl : tag);
this.setDrawable(view, imageUrl, tag, drawable);
}

@Override
public void onFailure(IOException e, String imageUrl, String tag) {
//Toast.makeText(mContext, e.toString(), Toast.LENGTH_SHORT).show();
}
}

// 调用该方法
ZImageLoader.getInstance().loadDrawable(item.getPicUrl(), item.getPicTag(), this.mOnPicLoadListener);


这是之前自己实现的简单的图片加载缓存类,不过今天开始尝试使用开源的《Android Universal Image Loader》,先把代码贴在这儿也算做个备份吧

github项目地址:https://github.com/nostra13/Android-Universal-Image-Loader

功能很完善、很强大了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐