该类实现的功能:
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
功能很完善、很强大了