閱讀585 返回首頁    go 阿裏雲 go 技術社區[雲棲]


異步圖片加載、內存、磁盤緩存

該類實現的功能:

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

  上一篇:go WIKIOI-1337 銀行裏的迷宮
  下一篇:go android自定義控件並添加屬性的方法以及示例