• android ListView异步加载图片(双缓存)


    首先声明,参考博客地址:http://www.iteye.com/topic/685986

    对于ListView,相信很多人都很熟悉,因为确实太常见了,所以,做的用户体验更好,就成了我们的追求。。。

    常见的ListView中很少全是文字的,一般都是图文共存的,而图片的来源是服务器端(很少有写在客户端的吧。。。考虑客户端的大小和更新的问题),所以,网络问题就成了图片是否能顺利加载成功的决定性因素了。大家都知道每次启动一个Android应用,都会启动一个UI主线程,主要是响应用户的交互,如果我们把不确定的获取网络图片的操作放在UI主线程,结果也就不确定了。。。当然,如果你网络足够好的话,应该问题不大,但是,网络谁能保证呢?所以就出现了“异步加载”的方法!

    我先叙述一下异步加载的原理,说的通俗一点就是UI主线程继续做与用户交互的响应监听和操作,而加载图片的任务交到其他线程中去做,当图片加载完成之后,再跟据某种机制(比如回调)绘制到要显示的控件中。

    首先,贴出AsyncBitmapLoader.java,这个类是关键,主要做的就是当加载图片的时候,去缓冲区查找,如果有的话,则立刻返回Bitmap对象,省掉再去网络服务器下载的时间和流量。

    <span style="font-size:18px;">package onerain.ald.async;
    
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.ref.SoftReference;
    import java.util.HashMap;
    
    import onerain.ald.common.HttpUtils;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.os.Handler;
    import android.os.Message;
    import android.widget.ImageView;
    
    /**
     * @author oneRain
     **/
    public class AsyncBitmapLoader
    {
        /**
         * 内存图片软引用缓冲
         */
        private HashMap<String, SoftReference<Bitmap>> imageCache = null;
        
        public AsyncBitmapLoader()
        {
            imageCache = new HashMap<String, SoftReference<Bitmap>>();
        }
        
        public Bitmap loadBitmap(final ImageView imageView, final String imageURL, final ImageCallBack imageCallBack)
        {
            //在内存缓存中,则返回Bitmap对象
            if(imageCache.containsKey(imageURL))
            {
                SoftReference<Bitmap> reference = imageCache.get(imageURL);
                Bitmap bitmap = reference.get();
                if(bitmap != null)
                {
                    return bitmap;
                }
            }
            else
            {
                /**
                 * 加上一个对本地缓存的查找
                 */
                String bitmapName = imageURL.substring(imageURL.lastIndexOf("/") + 1);
                File cacheDir = new File("/mnt/sdcard/test/");
                File[] cacheFiles = cacheDir.listFiles();
                int i = 0;
                for(; i<cacheFiles.length; i++)
                {
                    if(bitmapName.equals(cacheFiles[i].getName()))
                    {
                        break;
                    }
                }
                
                if(i < cacheFiles.length)
                {
                    return BitmapFactory.decodeFile("/mnt/sdcard/test/" + bitmapName);
                }
            }
            
            final Handler handler = new Handler()
            {
                /* (non-Javadoc)
                 * @see android.os.Handler#handleMessage(android.os.Message)
                 */
                @Override
                public void handleMessage(Message msg)
                {
                    // TODO Auto-generated method stub
                    imageCallBack.imageLoad(imageView, (Bitmap)msg.obj);
                }
            };
            
            //如果不在内存缓存中,也不在本地(被jvm回收掉),则开启线程下载图片
            new Thread()
            {
                /* (non-Javadoc)
                 * @see java.lang.Thread#run()
                 */
                @Override
                public void run()
                {
                    // TODO Auto-generated method stub
                    InputStream bitmapIs = HttpUtils.getStreamFromURL(imageURL);
                    
                    Bitmap bitmap = BitmapFactory.decodeStream(bitmapIs);
                    imageCache.put(imageURL, new SoftReference<Bitmap>(bitmap));
                    Message msg = handler.obtainMessage(0, bitmap);
                    handler.sendMessage(msg);
                    
                    File dir = new File("/mnt/sdcard/test/");
                    if(!dir.exists())
                    {
                        dir.mkdirs();
                    }
                    
                    File bitmapFile = new File("/mnt/sdcard/test/" + 
                            imageURL.substring(imageURL.lastIndexOf("/") + 1));
                    if(!bitmapFile.exists())
                    {
                        try
                        {
                            bitmapFile.createNewFile();
                        }
                        catch (IOException e)
                        {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                    FileOutputStream fos;
                    try
                    {
                        fos = new FileOutputStream(bitmapFile);
                        bitmap.compress(Bitmap.CompressFormat.JPEG, 
                                100, fos);
                        fos.close();
                    }
                    catch (FileNotFoundException e)
                    {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    catch (IOException e)
                    {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }.start();
            
            return null;
        }
        
        /**
         * 回调接口
         * @author onerain
         *
         */
        public interface ImageCallBack
        {
            public void imageLoad(ImageView imageView, Bitmap bitmap);
        }
    }
    </span>

    PS:我这里用到了两个缓冲,一是内存缓存,一个是本地缓存(即SD卡缓存),其中用到了SoftReference,这个类的主要作用是生成一个“软引用”,你可以认为是一种随时会由于JVM垃圾回收机制回收掉的Map对象(而平时我们所用到的引用不释放的话不会被JVM回收),之所以用到软引用,就是考虑到android对图片的缓存是有大小限制的,当超过这个大小时,就一定要释放,如果你用引用,保持不释放的话,那么FC(Force close)就离你不远了。。。我这里还用到了一个本地缓存的机制,是和参考博客不太一样的地方,只是提供一种思路,方法还没有完善(主要因为和服务器还没约定好关于图片的命名规则),主要作用是在用户浏览过大量图片之后(超过内存缓存容量之后),保留在本地,一是为了提高读取速度,二是可以减少流量消耗!

    这个类设计好之后,在自定义的Adapter当中比之前会有些不同,先上代码再解释

    <span style="font-size:18px;">package onerain.ald.adapter;
    
    import java.util.List;
    
    import onerain.ald.R;
    import onerain.ald.async.AsyncBitmapLoader;
    import onerain.ald.async.AsyncBitmapLoader.ImageCallBack;
    import onerain.ald.entity.BaseBookEntity;
    import onerain.ald.holder.ViewHolder;
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    /**
     * @author oneRain
     **/
    public class ListAdapter extends BaseAdapter
    {
        private Context context = null;
        private List<BaseBookEntity> bookList = null;
        
        private AsyncBitmapLoader asyncLoader = null;
        
        public ListAdapter(Context context, List<BaseBookEntity> bookList)
        {
            this.context = context;
            this.bookList = bookList;
            this.asyncLoader = new AsyncBitmapLoader();
        }
        
        @Override
        public int getCount()
        {
            // TODO Auto-generated method stub
            return bookList.size();
        }
    
        @Override
        public Object getItem(int position)
        {
            // TODO Auto-generated method stub
            return bookList.get(position);
        }
    
        @Override
        public long getItemId(int position)
        {
            // TODO Auto-generated method stub
            return position;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent)
        {
            // TODO Auto-generated method stub
            ViewHolder holder = null;
            
            if(convertView == null)
            {
                LayoutInflater inflater = LayoutInflater.from(context);
                convertView = inflater.inflate(R.layout.item, null);
                
                holder = new ViewHolder((ImageView)convertView.findViewById(R.id.imageView),
                        (TextView)convertView.findViewById(R.id.textView));
                convertView.setTag(holder);
            }
            else
            {
                holder = (ViewHolder) convertView.getTag();
            }
            
            ImageView imageView = holder.getImageView();
    
                    <span style="color:#FF0000;">imageView.setImageBitmap(null);</span>
    
    &nbsp;               //根据图片URL去查找内存缓存有没有对应的Bitmap对象,并传递回调方法,如果没有,则等下载完毕回调
            Bitmap bitmap = asyncLoader.loadBitmap(imageView, 
                    bookList.get(position).getBook_pic(),
                    new ImageCallBack()
                    {
                        @Override
                        public void imageLoad(ImageView imageView, Bitmap bitmap)
                        {
                            // TODO Auto-generated method stub
                            imageView.setImageBitmap(bitmap);
                        }
                    });
            
            if(bitmap == null)
            {
                imageView.setImageResource(R.drawable.ic_launcher);
            }
            else
            {
                imageView.setImageBitmap(bitmap);
            }
            
            holder.getTextView().setText(bookList.get(position).getTitle());
            
            return convertView;
        }
    }
    </span>

    在Adapter中,主要不同表现在

    public View getView(int position, View convertView, ViewGroup parent)方法中(我这里用到了一些优化方面的处理,会在其他时间再与大家分享,今天先不解释),和异步加载最相关的是这一段

    <span style="font-size:18px;">ImageView imageView = holder.getImageView();
            //根据图片URL去查找内存缓存有没有对应的Bitmap对象,并传递回调方法,如果没有,则等下载完毕回调
        Bitmap bitmap = asyncLoader.loadBitmap(imageView, 
                bookList.get(position).getBook_pic(),
                new ImageCallBack()
                {
                    @Override
                    public void imageLoad(ImageView imageView, Bitmap bitmap)
                    {
                        // TODO Auto-generated method stub
                        imageView.setImageBitmap(bitmap);
                    }
                });
            
        if(bitmap == null)
        {
            imageView.setImageResource(R.drawable.ic_launcher);
        }
        else
        {
            imageView.setImageBitmap(bitmap);
        }</span>

    asyncLoader是我们定义的异步加载类的对象,通过这个类的

    public Bitmap loadBitmap(final ImageView imageView, final String imageURL, final ImageCallBack imageCallBack)

    加载图片,传递参数也与参考博客有些不同,我觉得这样更好理解一下,就是要显示图片的URL链接,和图片要显示对应的控件,当然最重要的还有这个接口实现的回调对象,是在线程中下载完图片之后,用以加载图片的回调对象。而这个回调对象在传递的时候已经实现了接口方法,即将下载好的图片绘制在对应的控件之中

    <span style="font-size:18px;">imageView.setImageBitmap(bitmap);</span>
  • 相关阅读:
    Selenium3+python3自动化(三十九)--python3.7上SendKeys报错用PyUserInput取代
    Selenium3+python3自动化(三十八)--异常后截图(screnshot)、只截某个元素的图
    python学习6--python读取excel数据
    Selenium3+python3自动化(三十七)--捕获异常(NoSuchElementException)、try...except
    Selenium3+python3自动化(三十六)--expected_conditions模块 判断文本(text_to_be_present_in_element)
    Selenium3+python3自动化(三十五)--登录方法(参数化)
    Selenium3+python3自动化(三十四)--expected_conditions模块 判断弹出框存在(alert_is_present)
    Selenium3+python3自动化(三十三)--万能的js解决click()、clear()失效问题
    Selenium3+python3自动化(三十二)--4类32种定位方法(find_element_by_xx, find_elements_by_xx, find_element)
    Selenium3+python3自动化(三十一)--元素定位参数化(find_element)
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/4332524.html
Copyright © 2020-2023  润新知