麦洛开通博客以来,有一段时间没有更新博文了.主要是麦洛这段时间因项目开发实在太忙了.今天周六还在公司加班,苦逼程序猿都是这样生活的.

今天在做项目的时候,有一个实现异步加载图片的功能,虽然比较简单但还是记录一下吧.因为麦洛之前实现异步加载图片都是使用了AsynTask这个API,继续这个类,实现起来非常简单也很方便.在doInBackground()方法里实现下载逻辑.具体实现如下

实现逻辑是:先从内存中读取,如果内存中有这张图片,则直接使用;如果内存没有再到sdcard上读取,如果有则显示;如果sdcard上还没有则到网络上读取.内存中开启缓存是参考了网上的实现.麦洛在这里非常感谢喜欢分享的程序猿们.

public class ImageDownloader extends AsyncTask
 {    private static final String TAG = "ImageDownloader";    // 为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动)    private Map
> p_w_picpathCache = new HashMap
>();    /**     * 显示图片的控件     */    private ImageView mImageView;    public ImageDownloader(ImageView p_w_picpath) {        mImageView = p_w_picpath;    }    @Override    protected void onPreExecute() {        super.onPreExecute();    }    @Override    protected Object doInBackground(String... params) {        // Log.i("ImageDownloader", "loading p_w_picpath...");        String url = params[0];        Drawable drawable = null;        try {            if (!"".equals(url) && url != null) {                String fileName = url.hashCode()+".jpg";                // 如果缓存过就从缓存中取出数据                if (p_w_picpathCache.containsKey(fileName)) {                    SoftReference
 softReference = p_w_picpathCache.get(fileName);                    drawable = softReference.get();                    if (drawable != null) {                        return drawable;                    }                }                File dir = new File(FileConstant.IMAGE_FILE_PATH);                if (!dir.exists()) {                    boolean m = dir.mkdirs();                }                File file = new File(dir, fileName);                if (file.exists() && file.length() > 0) {                    Log.i(TAG, "load p_w_picpath from sd card");                    // 如果文件存在则直接读取sdcard                    drawable = readFromSdcard(file);                } else {                    //file.createNewFile();                    Log.i(TAG, "load p_w_picpath from network");                    URL p_w_picpathUrl = new URL(url);                    // 写入sdcard                    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {                        saveImageFile(p_w_picpathUrl, file);                        drawable = Drawable.createFromStream(new FileInputStream(file), fileName);                    }else{                        //直接从流读取                        drawable = Drawable.createFromStream(p_w_picpathUrl.openStream(), fileName);                    }                }                if(drawable!=null){                    //保存在缓存中                    p_w_picpathCache.put(fileName, new SoftReference
(drawable));                }            }        } catch (Exception e) {            e.printStackTrace();        }         return drawable;    }    /**     * save p_w_picpath*/    private void saveImageFile(URL url, File file) {        FileOutputStream out = null;        InputStream in = null;        try {            file.deleteOnExit();            out = new FileOutputStream(file);            in = url.openStream();            byte[] buf = new byte[1024];            int len = -1;            while((len = in.read(buf))!=-1){                out.write(buf, 0, len);                out.flush();            }        } catch (Exception e) {            e.printStackTrace();        } finally {            if(out!=null){                try {                    out.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if(in!=null){                try {                    in.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }    /**     * 从sdcard中获取图片*/    private Drawable readFromSdcard(File file) throws Exception {        FileInputStream in = new FileInputStream(file);        return Drawable.createFromStream(in, file.getName());    }    @Override    protected void onPostExecute(Object result) {        super.onPostExecute(result);        Drawable drawable = (Drawable) result;        if (mImageView != null && drawable != null) {            mImageView.setBackgroundDrawable(drawable);        }    }    @Override    protected void onProgressUpdate(Integer... values) {        super.onProgressUpdate(values);    }    @Override    protected void onCancelled() {        super.onCancelled();    }}

使用时:

ImageDownloader loader = new ImageDownloader(p_w_picpathView);loader.execute(url);

其实这样的话,还有一些隐患的,就是说这个类实现还是有些问题的.比如每次都在p_w_picpathView中设置网络上的图片时,其实是没有使用到这个类里面的内存缓存的,就是p_w_picpathCache

Map
> p_w_picpathCache = new HashMap
>();

因为每次设置p_w_picpathView的时候,都是new了一个ImageDownloader的对象.所以每个ImageDownloader对象里面都是独立的一个p_w_picpathCache.

      另外,AsynTask也是一个线程.而每次使用都开一个线程来load 图片,对线程个数没有进行显示,毕竟线程数目还是有限制的.

所以麦洛今天发现了这个问题,于是参考了别人的实现,使用了线程池,实现逻辑也上面的代码一样,先从内存读取,如果没有到sdcard读取,如果还是没有,则是网络读取;实现没有使用AsynTask,具体代码如下:

/** * 异步加载图片,并将图片设置到ImageView控件中*/public class ImageDownloader extends AsyncTask
 {    private static final String TAG = "ImageDownloader";    // 为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动)    private Map
> p_w_picpathCache = new HashMap
>();    /**     * 显示图片的控件     */    private ImageView mImageView;    public ImageDownloader(ImageView p_w_picpath) {        mImageView = p_w_picpath;    }    @Override    protected void onPreExecute() {        super.onPreExecute();    }    @Override    protected Object doInBackground(String... params) {        // Log.i("ImageDownloader", "loading p_w_picpath...");        String url = params[0];        Drawable drawable = null;        try {            if (!"".equals(url) && url != null) {                String fileName = url.hashCode()+".jpg";                // 如果缓存过就从缓存中取出数据                if (p_w_picpathCache.containsKey(fileName)) {                    SoftReference
 softReference = p_w_picpathCache.get(fileName);                    drawable = softReference.get();                    if (drawable != null) {                        return drawable;                    }                }                File dir = new File(FileConstant.IMAGE_FILE_PATH);                if (!dir.exists()) {                    boolean m = dir.mkdirs();                }                File file = new File(dir, fileName);                if (file.exists() && file.length() > 0) {                    Log.i(TAG, "load p_w_picpath from sd card");                    // 如果文件存在则直接读取sdcard                    drawable = readFromSdcard(file);                } else {                    //file.createNewFile();                    Log.i(TAG, "load p_w_picpath from network");                    URL p_w_picpathUrl = new URL(url);                    // 写入sdcard                    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {                        saveImageFile(p_w_picpathUrl, file);                        drawable = Drawable.createFromStream(new FileInputStream(file), fileName);                    }else{                        //直接从流读取                        drawable = Drawable.createFromStream(p_w_picpathUrl.openStream(), fileName);                    }                }                if(drawable!=null){                    //保存在缓存中                    p_w_picpathCache.put(fileName, new SoftReference
(drawable));                }            }        } catch (Exception e) {            e.printStackTrace();        }         return drawable;    }    /**     * save p_w_picpath*/    private void saveImageFile(URL url, File file) {        FileOutputStream out = null;        InputStream in = null;        try {            file.deleteOnExit();            out = new FileOutputStream(file);            in = url.openStream();            byte[] buf = new byte[1024];            int len = -1;            while((len = in.read(buf))!=-1){                out.write(buf, 0, len);                out.flush();            }        } catch (Exception e) {            e.printStackTrace();        } finally {            if(out!=null){                try {                    out.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if(in!=null){                try {                    in.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }    /**     * 从sdcard中获取图片  */    private Drawable readFromSdcard(File file) throws Exception {        FileInputStream in = new FileInputStream(file);        return Drawable.createFromStream(in, file.getName());    }    @Override    protected void onPostExecute(Object result) {        super.onPostExecute(result);        Drawable drawable = (Drawable) result;        if (mImageView != null && drawable != null) {            mImageView.setBackgroundDrawable(drawable);        }    }    @Override    protected void onProgressUpdate(Integer... values) {        super.onProgressUpdate(values);    }    @Override    protected void onCancelled() {        super.onCancelled();    }}

这个ImageDownloader2的使用也很简单

public class ImageUtil {    /**     * p_w_picpath loader     */    static ImageDownloader2 loader = null;        /**     * load p_w_picpath*/    public static void loadImage(String url,final ImageView p_w_picpathView){        if(loader == null){            loader = new ImageDownloader2();        }        loader.loadDrawable(url, new ImageCallback() {                        @Override            public void p_w_picpathLoaded(Drawable p_w_picpathDrawable) {                if(p_w_picpathDrawable!=null){                    p_w_picpathView.setBackgroundDrawable(p_w_picpathDrawable);                }            }        });    }    }

每次在使用是需要调用ImageUtil.loadImage(url,p_w_picpathView)将图片url已经需要显示图片的控件ImageView的引用传入就可以了.