異步圖片加載、內存、磁盤緩存
該類實現的功能:
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
功能很完善、很強大了
最後更新:2017-04-03 12:55:26