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

该类实现的功能:

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

功能很完善、很强大了








posted @ 2014-03-05 17:08  神神的蜗牛  阅读(245)  评论(0编辑  收藏  举报