zoukankan      html  css  js  c++  java
  • android ListView异步加载图片(双缓存)


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

     

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

     

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

     

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

     

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

     

    1. <span style="font-size:18px;">package onerain.ald.async;  
    2.   
    3. import java.io.File;  
    4. import java.io.FileNotFoundException;  
    5. import java.io.FileOutputStream;  
    6. import java.io.IOException;  
    7. import java.io.InputStream;  
    8. import java.lang.ref.SoftReference;  
    9. import java.util.HashMap;  
    10.   
    11. import onerain.ald.common.HttpUtils;  
    12. import android.graphics.Bitmap;  
    13. import android.graphics.BitmapFactory;  
    14. import android.os.Handler;  
    15. import android.os.Message;  
    16. import android.widget.ImageView;  
    17.   
    18. /** 
    19.  * @author oneRain 
    20.  **/  
    21. public class AsyncBitmapLoader  
    22. {  
    23.     /** 
    24.      * 内存图片软引用缓冲 
    25.      */  
    26.     private HashMap<String, SoftReference<Bitmap>> imageCache = null;  
    27.       
    28.     public AsyncBitmapLoader()  
    29.     {  
    30.         imageCache = new HashMap<String, SoftReference<Bitmap>>();  
    31.     }  
    32.       
    33.     public Bitmap loadBitmap(final ImageView imageView, final String imageURL, final ImageCallBack imageCallBack)  
    34.     {  
    35.         //在内存缓存中,则返回Bitmap对象  
    36.         if(imageCache.containsKey(imageURL))  
    37.         {  
    38.             SoftReference<Bitmap> reference = imageCache.get(imageURL);  
    39.             Bitmap bitmap = reference.get();  
    40.             if(bitmap != null)  
    41.             {  
    42.                 return bitmap;  
    43.             }  
    44.         }  
    45.         else  
    46.         {  
    47.             /** 
    48.              * 加上一个对本地缓存的查找 
    49.              */  
    50.             String bitmapName = imageURL.substring(imageURL.lastIndexOf("/") + 1);  
    51.             File cacheDir = new File("/mnt/sdcard/test/");  
    52.             File[] cacheFiles = cacheDir.listFiles();  
    53.             int i = 0;  
    54.             for(; i<cacheFiles.length; i++)  
    55.             {  
    56.                 if(bitmapName.equals(cacheFiles[i].getName()))  
    57.                 {  
    58.                     break;  
    59.                 }  
    60.             }  
    61.               
    62.             if(i < cacheFiles.length)  
    63.             {  
    64.                 return BitmapFactory.decodeFile("/mnt/sdcard/test/" + bitmapName);  
    65.             }  
    66.         }  
    67.           
    68.         final Handler handler = new Handler()  
    69.         {  
    70.             /* (non-Javadoc) 
    71.              * @see android.os.Handler#handleMessage(android.os.Message) 
    72.              */  
    73.             @Override  
    74.             public void handleMessage(Message msg)  
    75.             {  
    76.                 // TODO Auto-generated method stub  
    77.                 imageCallBack.imageLoad(imageView, (Bitmap)msg.obj);  
    78.             }  
    79.         };  
    80.           
    81.         //如果不在内存缓存中,也不在本地(被jvm回收掉),则开启线程下载图片  
    82.         new Thread()  
    83.         {  
    84.             /* (non-Javadoc) 
    85.              * @see java.lang.Thread#run() 
    86.              */  
    87.             @Override  
    88.             public void run()  
    89.             {  
    90.                 // TODO Auto-generated method stub  
    91.                 InputStream bitmapIs = HttpUtils.getStreamFromURL(imageURL);  
    92.                   
    93.                 Bitmap bitmap = BitmapFactory.decodeStream(bitmapIs);  
    94.                 imageCache.put(imageURL, new SoftReference<Bitmap>(bitmap));  
    95.                 Message msg = handler.obtainMessage(0, bitmap);  
    96.                 handler.sendMessage(msg);  
    97.                   
    98.                 File dir = new File("/mnt/sdcard/test/");  
    99.                 if(!dir.exists())  
    100.                 {  
    101.                     dir.mkdirs();  
    102.                 }  
    103.                   
    104.                 File bitmapFile = new File("/mnt/sdcard/test/" +   
    105.                         imageURL.substring(imageURL.lastIndexOf("/") + 1));  
    106.                 if(!bitmapFile.exists())  
    107.                 {  
    108.                     try  
    109.                     {  
    110.                         bitmapFile.createNewFile();  
    111.                     }  
    112.                     catch (IOException e)  
    113.                     {  
    114.                         // TODO Auto-generated catch block  
    115.                         e.printStackTrace();  
    116.                     }  
    117.                 }  
    118.                 FileOutputStream fos;  
    119.                 try  
    120.                 {  
    121.                     fos = new FileOutputStream(bitmapFile);  
    122.                     bitmap.compress(Bitmap.CompressFormat.JPEG,   
    123.                             100, fos);  
    124.                     fos.close();  
    125.                 }  
    126.                 catch (FileNotFoundException e)  
    127.                 {  
    128.                     // TODO Auto-generated catch block  
    129.                     e.printStackTrace();  
    130.                 }  
    131.                 catch (IOException e)  
    132.                 {  
    133.                     // TODO Auto-generated catch block  
    134.                     e.printStackTrace();  
    135.                 }  
    136.             }  
    137.         }.start();  
    138.           
    139.         return null;  
    140.     }  
    141.       
    142.     /** 
    143.      * 回调接口 
    144.      * @author onerain 
    145.      * 
    146.      */  
    147.     public interface ImageCallBack  
    148.     {  
    149.         public void imageLoad(ImageView imageView, Bitmap bitmap);  
    150.     }  
    151. }  
    152. </span>  

     

     

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

     

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

     

    1. <span style="font-size:18px;">package onerain.ald.adapter;  
    2.   
    3. import java.util.List;  
    4.   
    5. import onerain.ald.R;  
    6. import onerain.ald.async.AsyncBitmapLoader;  
    7. import onerain.ald.async.AsyncBitmapLoader.ImageCallBack;  
    8. import onerain.ald.entity.BaseBookEntity;  
    9. import onerain.ald.holder.ViewHolder;  
    10. import android.content.Context;  
    11. import android.graphics.Bitmap;  
    12. import android.view.LayoutInflater;  
    13. import android.view.View;  
    14. import android.view.ViewGroup;  
    15. import android.widget.BaseAdapter;  
    16. import android.widget.ImageView;  
    17. import android.widget.TextView;  
    18.   
    19. /** 
    20.  * @author oneRain 
    21.  **/  
    22. public class ListAdapter extends BaseAdapter  
    23. {  
    24.     private Context context = null;  
    25.     private List<BaseBookEntity> bookList = null;  
    26.       
    27.     private AsyncBitmapLoader asyncLoader = null;  
    28.       
    29.     public ListAdapter(Context context, List<BaseBookEntity> bookList)  
    30.     {  
    31.         this.context = context;  
    32.         this.bookList = bookList;  
    33.         this.asyncLoader = new AsyncBitmapLoader();  
    34.     }  
    35.       
    36.     @Override  
    37.     public int getCount()  
    38.     {  
    39.         // TODO Auto-generated method stub  
    40.         return bookList.size();  
    41.     }  
    42.   
    43.     @Override  
    44.     public Object getItem(int position)  
    45.     {  
    46.         // TODO Auto-generated method stub  
    47.         return bookList.get(position);  
    48.     }  
    49.   
    50.     @Override  
    51.     public long getItemId(int position)  
    52.     {  
    53.         // TODO Auto-generated method stub  
    54.         return position;  
    55.     }  
    56.   
    57.     @Override  
    58.     public View getView(int position, View convertView, ViewGroup parent)  
    59.     {  
    60.         // TODO Auto-generated method stub  
    61.         ViewHolder holder = null;  
    62.           
    63.         if(convertView == null)  
    64.         {  
    65.             LayoutInflater inflater = LayoutInflater.from(context);  
    66.             convertView = inflater.inflate(R.layout.item, null);  
    67.               
    68.             holder = new ViewHolder((ImageView)convertView.findViewById(R.id.imageView),  
    69.                     (TextView)convertView.findViewById(R.id.textView));  
    70.             convertView.setTag(holder);  
    71.         }  
    72.         else  
    73.         {  
    74.             holder = (ViewHolder) convertView.getTag();  
    75.         }  
    76.           
    77.         ImageView imageView = holder.getImageView();  
    78.   
    79.                 <span style="color:#FF0000;">imageView.setImageBitmap(null);</span>  
    80.   
    81.                 //根据图片URL去查找内存缓存有没有对应的Bitmap对象,并传递回调方法,如果没有,则等下载完毕回调  
    82.         Bitmap bitmap = asyncLoader.loadBitmap(imageView,   
    83.                 bookList.get(position).getBook_pic(),  
    84.                 new ImageCallBack()  
    85.                 {  
    86.                     @Override  
    87.                     public void imageLoad(ImageView imageView, Bitmap bitmap)  
    88.                     {  
    89.                         // TODO Auto-generated method stub  
    90.                         imageView.setImageBitmap(bitmap);  
    91.                     }  
    92.                 });  
    93.           
    94.         if(bitmap == null)  
    95.         {  
    96.             imageView.setImageResource(R.drawable.ic_launcher);  
    97.         }  
    98.         else  
    99.         {  
    100.             imageView.setImageBitmap(bitmap);  
    101.         }  
    102.           
    103.         holder.getTextView().setText(bookList.get(position).getTitle());  
    104.           
    105.         return convertView;  
    106.     }  
    107. }  
    108. </span>  

     

    在Adapter中,主要不同表现在

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

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

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

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

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

    1. <span style="font-size:18px;">imageView.setImageBitmap(bitmap);</span>  

     

    好了,今天就分享到这里,有言辞不清楚的地方欢迎留言!

  • 相关阅读:
    [Intellij] 软件设置和常用快捷键
    [Intellij] Project Structure 配置说明
    [日志log] 常用log日志记录方式对比和详解
    [J2EE]web.xml各个版本模板
    [技术选型] CDH-Cloudera Distribution Hadoop
    [技术选型] dubbo
    [技术选型] spring boot
    [hbase] HBase内置过滤器的一些总结
    [zookeeper] Zookeeper伪分布式集群配置
    [maven] settings 文件 本地maven仓库
  • 原文地址:https://www.cnblogs.com/firecode/p/2866386.html
Copyright © 2011-2022 走看看