異步圖片加載、內存、磁盤緩存
該類實現的功能:
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