Android 实现异步加载图片

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

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

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

public class ImageDownloader extends AsyncTask<String, Integer, Object> {    private static final String TAG = "ImageDownloader";    // 为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动)
    private Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();    /**
     * 显示图片的控件     */
    private ImageView mImageView;    public ImageDownloader(ImageView image) {
        mImageView = image;
    }

    @Override    protected void onPreExecute() {        super.onPreExecute();
    }

    @Override    protected Object doInBackground(String... params) {        // Log.i("ImageDownloader", "loading image...");
        String url = params[0];
        Drawable drawable = null;        try {            if (!"".equals(url) && url != null) {
                String fileName = url.hashCode()+".jpg";                // 如果缓存过就从缓存中取出数据
                if (imageCache.containsKey(fileName)) {
                    SoftReference<Drawable> softReference = imageCache.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 image from sd card");                    // 如果文件存在则直接读取sdcard
                    drawable = readFromSdcard(file);
                } else {                    //file.createNewFile();
                    Log.i(TAG, "load image from network");
                    URL imageUrl = new URL(url);                    // 写入sdcard
                    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                        saveImageFile(imageUrl, file);
                        drawable = Drawable.createFromStream(new FileInputStream(file), fileName);
                    }else{                        //直接从流读取
                        drawable = Drawable.createFromStream(imageUrl.openStream(), fileName);
                    }
                }                if(drawable!=null){                    //保存在缓存中
                    imageCache.put(fileName, new SoftReference<Drawable>(drawable));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } 
        return drawable;
    }    /**
     * save image*/
    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(imageView);
loader.execute(url);

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

Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();

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

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

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

/**
 * 异步加载图片,并将图片设置到ImageView控件中*/public class ImageDownloader extends AsyncTask<String, Integer, Object> {    private static final String TAG = "ImageDownloader";    // 为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动)
    private Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();    /**
     * 显示图片的控件     */
    private ImageView mImageView;    public ImageDownloader(ImageView image) {
        mImageView = image;
    }

    @Override    protected void onPreExecute() {        super.onPreExecute();
    }

    @Override    protected Object doInBackground(String... params) {        // Log.i("ImageDownloader", "loading image...");
        String url = params[0];
        Drawable drawable = null;        try {            if (!"".equals(url) && url != null) {
                String fileName = url.hashCode()+".jpg";                // 如果缓存过就从缓存中取出数据
                if (imageCache.containsKey(fileName)) {
                    SoftReference<Drawable> softReference = imageCache.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 image from sd card");                    // 如果文件存在则直接读取sdcard
                    drawable = readFromSdcard(file);
                } else {                    //file.createNewFile();
                    Log.i(TAG, "load image from network");
                    URL imageUrl = new URL(url);                    // 写入sdcard
                    if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                        saveImageFile(imageUrl, file);
                        drawable = Drawable.createFromStream(new FileInputStream(file), fileName);
                    }else{                        //直接从流读取
                        drawable = Drawable.createFromStream(imageUrl.openStream(), fileName);
                    }
                }                if(drawable!=null){                    //保存在缓存中
                    imageCache.put(fileName, new SoftReference<Drawable>(drawable));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } 
        return drawable;
    }    /**
     * save image*/
    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 {    /**
     * image loader     */
    static ImageDownloader2 loader = null;    
    /**
     * load image*/
    public static void loadImage(String url,final ImageView imageView){        if(loader == null){
            loader = new ImageDownloader2();
        }
        loader.loadDrawable(url, new ImageCallback() {
            
            @Override            public void imageLoaded(Drawable imageDrawable) {                if(imageDrawable!=null){
                    imageView.setBackgroundDrawable(imageDrawable);
                }
            }
        });
    }
    
}

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

时间: 2024-08-18 13:19:47

Android 实现异步加载图片的相关文章

android listview 异步加载图片并防止错位

网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题. 我简单分析一下: 当重用 convertView 时,最初一屏显示 7 条记录, getView 被调用 7 次,创建了 7 个 convertView. 当 Item1 划出屏幕, Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例, Ite

android listview 异步加载图片并防止错位+双缓存

网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题. 我简单分析一下: 当重用 convertView 时,最初一屏显示 7 条记录, getView 被调用 7 次,创建了 7 个 convertView. 当 Item1 划出屏幕, Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例, Ite

又优化了一下 Android ListView 异步加载图片

写这篇文章并不是教大家怎么样用listview异步加载图片,因为这样的文章在网上已经有很多了,比如这位仁兄写的就很好: http://www.iteye.com/topic/685986 我也是因为看了这篇文章而受到了启发. 先说说这篇文章的优点把,开启线程异步加载图片,然后刷新UI显示图片,而且通过弱引用缓存网络加载的图片,节省了再次连接网络的开销. 这样做无疑是非常可取的方法,但是加载图片时仍然会感觉到轻微的卡屏现象,特别是listview里的item在进行快速滑动的时候. 我找了一下原因,

android listview 异步加载图片并防止错位 解决办法

网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题. 我简单分析一下: 当重用 convertView 时,最初一屏显示 7 条记录, getView 被调用 7 次,创建了 7 个 convertView. 当 Item1 划出屏幕, Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例, Ite

Android ListView异步加载图片乱序问题,原因分析及解决方案

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/45586553 在Android所有系统自带的控件当中,ListView这个控件算是用法比较复杂的了,关键是用法复杂也就算了,它还经常会出现一些稀奇古怪的问题,让人非常头疼.比如说在ListView中加载图片,如果是同步加载图片倒还好,但是一旦使用异步加载图片那么问题就来了,这个问题我相信很多Android开发者都曾经遇到过,就是异步加载图片会出现错位乱序的情况.遇到这个问题时,不

android listview异步加载图片错位,重复,闪烁分析以及解决方案

我们在使用listview异步加载图片 的时候,在快速滑动或者网络不好的情况下,会出现图片错位,重复,闪烁等问题,其实这些问题总结起来就是一个问题, 比如listview上有100个item,一屏只显示10个item,我们知道getView()中converView是用来复用view对象的,因为一个item的view对象,而imageview控件就是view通过findViewById()获得的,而我们在复用view对象时,也就是说这个imageview也被复用了,比如第11个item的view

Android ListView异步加载图片错位、重复、闪烁分析以及解决方案

我们在使用ListView异步加载图片的时候,在快速滑动或者网络不好的情况下,会出现图片错位.重复.闪烁等问题,其实这些问题总结起来就是一个问题,我们需要对这些问题进行ListView的优化. 比如ListView上有100个Item,一屏只显示10个Item,我们知道getView()中convertView是用来复用View对象的,因为一个Item的对应一个View对象,而ImageView控件就是View对象通过findViewById()获得的,而我们在复用View对象时,同时这个Ima

Android:ViewPager扩展详解——带有导航的ViewPagerIndicator(附带图片缓存,异步加载图片)

大家都用过viewpager了, github上有对viewpager进行扩展,导航风格更加丰富,这个开源项目是ViewPagerIndicator,很好用,但是例子比较简单,实际用起来要进行很多扩展,比如在fragment里进行图片缓存和图片异步加载. 下面是ViewPagerIndicator源码运行后的效果,大家也都看过了,我多此一举截几张图: 下载源码请点击这里 ===========================================华丽的分割线==============

android开发干货:实现listview异步加载图片

针对listview异步加载图片这个问题,麦子学院android开发老师讲了一种非常实用的方法,麦子学院android开发老师说凡是是要通过网络获取图片资源一般使用这种方法比较好,用户体验好,下面就说实现方法,先贴上主方法的代码: package cn.wangmeng.test; import java.io.IOException; import java.io.InputStream; import java.lang.ref.SoftReference; import java.net.