• (BUG已修改,最优化)安卓ListView异步加载网络图片与缓存软引用图片,线程池,只加载当前屏之说明


    原文:http://blog.csdn.net/java_jh/article/details/20068915

    迟点出更新的.这个还有BUG.因为软引应不给力了.2.3之后

    前几天的原文有一个线程管理与加载源过多,造成浪费流量的问题.下面对这进下改进的一些说明(红色为新加)

    这两天一直在优化这个问题.google也很多种做法.但发现都是比较不全面.

    比如: 

    一些只实现了异步加载,却没有线程池与软引用.

     一些是用AsynTast的,

    一些有了线程池但加载所有的图片,这样造成具大资源浪费

    一些是用显示当前屏的item,但却用的是线程等待唤醒的方法.(这个不推荐)

    AsynTast这个开始我也是用它的.后来发现很不理想呀

    .于是今天就总结了一个思路.同时把前两天的代码改一下

    部份代码来自网络

    最终思路:  子线程加载网络图片并用缓存图片软引用..线程池管理子线程...根据当前屏第一item的potion与最后item的position加载当前屏显示的item图片.

    拖动过程中的不加载..根据onScrollListener来监听是否停止拖动. l

    istView.getFirstVisiblePosition()  当前屏第一条的下标

    listView.getLastVisiblePosition();当前屏最后一条下标

    最近开发个应用,里面大量的activity要用到listView这个控件.由于为了更加美观显示,就要自定义一个.

    这下问题出来了.因为是获取网络图片.按传统的做法没办法及时加载对应的图片或者图片错位.
    在网上找了很久.也抠了一天的源码..发现网上的都没有比较系统的说明.所以这里整理一下.
    方便以后自己回看.
    ========================
    先说一下思路: 我的理解为---- 因为要网络操作.所以加载图片在子线程中. 有延迟.但主线程都不等你子线程是否获取结果.它就走下去了.这样setImageDrawable(这里当然是没有了).
    所以就会在你到ListView加载完时.看不到图片的原因. 
    那么.在加载图片的子线程中,如果获取到图片之后.就handler发送一个信息到主线程.让它根据当前行的下标(postion)来更新图片.我管你主线程跟到哪了.管你等不等我.
    反正我慢慢地下载图..下载到了我再叫你更新.
    ========================
    首先说自定义的SimpleAdapter..
    这里的传统做法大家都应懂的了.就是那个getView() 方法可以有点难理解
    简单地说. 就是加载每一行数据(单行ListView).就调一次getView() 
    public View getView(int position, View convertView, ViewGroup parent){}
    position: 这个参数是指当前一行的下标. (从0开始的);
    converTiew: 是可以理解为当前一屏..(不知对不对.我是这样理解的.)第一次执行convertView,如果是第一次就进行布局资源的创建操作 
    如果拖进屏幕时.就可以复用到它了.不用每一屏都新建一个.这里下面代码里有说明
    到图片加载了.我们定义一个图片加载的类.用一个静态方法来获取图片的Drawable
    但由于优化内存使用,为了ListView加载了太多图片在内存中.那么.我们就进行缓存软引用机制来管理图片.
    说得这么绕..无非就是指, 把得到的Drawable变成一个软引用.然后再把它放进map中.让系统自己的决定什么时候回收内存中的图片.
    关于软引用...我个人的用法就是.但到一个drawable之后.马上new SoftReference<Drawable> (drawable) 存到map 中...那什么时候变回普通drawable呢
    我认为当要从map中取出来之后.第一步就要变回普通的drawable(--softReference.get()--).这样的话.当我回来拖进listView时...就不会因为系统清理我的软引用导致看不到图了

    下面上代码..先上异步获取图片的类

    图片加载器就加了个线程池.

    AsyncImageTask.java

    [java] view plaincopy
     
    1. package com.naxieshu.util;  
    2.   
    3. import java.io.IOException;  
    4. import java.io.InputStream;  
    5. import java.lang.ref.SoftReference;  
    6. import java.net.URL;  
    7. import java.util.HashMap;  
    8. import java.util.Map;  
    9.   
    10. import android.graphics.drawable.Drawable;  
    11. import android.os.Handler;  
    12. import android.os.Message;  
    13.   
    14. /** 
    15.  * 异步加截图片类 
    16.  * @author Hai Jiang 
    17.  * @Email 672335219@qq.ciom 
    18.  * @Data 2014-2-26 
    19.  */  
    20. public class AsyncImageTask {  
    21.     <span style="color:#ff0000;">//开线程池  
    22.     ExecutorService executorService =  Executors.newCachedThreadPool()</span>;  
    23.     //缓存图片 把图片的软引用放到map中  
    24.     private Map<String, SoftReference<Drawable>> imageMap;  
    25.     //构造器  
    26.     public AsyncImageTask() {    
    27.             super();    
    28.             this.imageMap = new HashMap<String, SoftReference<Drawable>>();    
    29.         }   
    30.       
    31.     //ID为标记,标记哪条记录image . 这个ID来自于自定义adapter的getView()方法中其中一个参数position  
    32.     public Drawable loadImage(final int id, final String imageUrl,   
    33.             final ImageCallback callback){  
    34.           
    35.         //先看缓存(Map)中是否存在  
    36.         if(imageMap.containsKey(imageUrl)){  
    37.             SoftReference<Drawable> softReference = imageMap.get(imageUrl);  
    38.             if( softReference != null){  
    39.                            Drawable draeable = softReferenct.get();  
    40.                if(drawable != null){  
    41.                                 callback.imageLoaded(drawable, id);  
    42.                 return drawable;  
    43.                }  
    44.                 }  
    45.         }  
    46.           
    47.         //主线程更新图片  
    48.         final Handler handler = new Handler() {    
    49.                     public void handleMessage(Message message) {    
    50.                             callback.imageLoaded((Drawable) message.obj, id);    
    51.                     }    
    52.             };  
    53.         //加载图片的线程  
    54.             executorService.submit(   
    55.         //加载图片的线程  
    56.         new Thread() {    
    57.                 public void run() {    
    58.                     //加载图片    
    59.                     Drawable drawable = AsyncImageTask.loadImageByUrl(imageUrl);    
    60.                     //加入缓存集合中 注意  这里就要把得到的图片变成软引用放到map中了  
    61.                     imageMap.put(imageUrl, new SoftReference<Drawable>(drawable));    
    62.                     //通知消息主线程更新UI  . 这里就是是否能异步刷新的留意点.  
    63.                     Message message = handler.obtainMessage(0, drawable);    
    64.                     handler.sendMessage(message);    
    65.                 }    
    66.             });  
    67.         return null;  
    68.         //到这里就获取图片的静态方法就完了  
    69.     }  
    70.       
    71.     //根据图片地址加载图片,并保存为Drawable  
    72.     //这里不用说了吧.都是一些基本的.从API从可以看  
    73.     public static Drawable loadImageByUrl(String imageUrl){  
    74.         URL url = null;  
    75.         InputStream inputStream = null;  
    76.         try {  
    77.             url = new URL(Constant.TARGETURL+imageUrl);  
    78.             inputStream = (InputStream) url.getContent();    
    79.             Drawable drawable = Drawable.createFromStream(inputStream,"src");  
    80.             return drawable;  
    81.         } catch (Exception e) {  
    82.             e.printStackTrace();  
    83.         } finally {  
    84.             try {  
    85.                 if(inputStream != null)  
    86.                     inputStream.close();  
    87.             } catch (IOException e) {  
    88.                 e.printStackTrace();  
    89.             }  
    90.         }  
    91.         return null;  
    92.     }  
    93.       
    94.     //利用接口回调,更新图片UI    
    95.         public interface ImageCallback {    
    96.             public void imageLoaded(Drawable obj, int id);    
    97.         }   
    98.       
    99. }  




    这里是自定义adapter类(这里因为主要是加个停止监听)

    MyListAdapter.java

    [java] view plaincopy
     
    1. package com.naxieshu.adapter;  
    2.   
    3. import java.lang.ref.SoftReference;  
    4. import java.util.HashMap;  
    5. import java.util.List;  
    6. import java.util.Map;  
    7.   
    8. import android.content.ClipData.Item;  
    9. import android.content.Context;  
    10. import android.graphics.Bitmap;  
    11. import android.graphics.drawable.Drawable;  
    12. import android.util.Log;  
    13. import android.view.LayoutInflater;  
    14. import android.view.View;  
    15. import android.view.ViewGroup;  
    16. import android.widget.AbsListView;  
    17. import android.widget.BaseAdapter;  
    18. import android.widget.ImageView;  
    19. import android.widget.ListView;  
    20. import android.widget.SimpleAdapter;  
    21. import android.widget.TextView;  
    22. import android.widget.AbsListView.OnScrollListener;  
    23.   
    24. import com.naxieshu.activity.FindActivity;  
    25. import com.naxieshu.activity.R;  
    26. import com.naxieshu.domain.Book;  
    27. import com.naxieshu.util.AsyncImageTask;  
    28. import com.naxieshu.util.AsyncImageTask.ImageCallback;  
    29. import com.naxieshu.util.ImageUtil;  
    30.   
    31. /** 
    32.  * 自定义List内容控件 
    33.  * @author Hai Jiang 
    34.  * @Email 672335219@qq.ciom 
    35.  * @Data 2014-2-26 
    36.  */  
    37. public class MyListAdapter extends SimpleAdapter{  
    38.     public List<? extends Map<String, ?>> data;  
    39.     private LayoutInflater inflater;  
    40.     /**异步加载图片实例*/  
    41.     private AsyncImageTask imageTask;  
    42.     /**被绑定对象*/  
    43.     private ListView listView;  
    44.     /**Item对象集*/  
    45.     HashMap<String, Object> itemMap =  new HashMap<String, Object>();  
    46.       
    47.     public MyListAdapter(final ListView listView,Context context,  
    48.             List<? extends Map<String, ?>> data) {  
    49.         super(context, data, 0, null, null);  
    50.         this.data = data;  
    51.         this.listView = listView;    
    52.         inflater = LayoutInflater.from(context);    
    53.         imageTask = new AsyncImageTask();  
    54.         /**注册监听事件*/  
    55.         listView.setOnScrollListener(onScrollListener);  
    56.           
    57.     }  
    58.     /** 
    59.      * 在创建View资源对象的时候提供效率的缓存策略 
    60.      */  
    61.     class ViewHold{  
    62.         //book.cover  
    63.         public ImageView image;  
    64.         //book.title book.shortIntro  
    65.         public TextView namtView,idView,introView;  
    66.     }  
    67.     ViewHold hold =null;  
    68.     @Override  
    69.     public View getView(int position, View convertView, ViewGroup parent) {  
    70.         //数据源中与当前item对应的数据  
    71.         Book book = (Book) data.get(position).get(position+"");  
    72.         //判断是否第一次执行convertView,如果是第一次就进行布局资源的创建操作  
    73.           
    74.         if (convertView == null){  
    75.             hold = new ViewHold();  
    76.             //填充加载布局资源  
    77.             convertView = inflater.inflate(R.layout.activity_find_listview, null);  
    78.             hold.image = (ImageView)convertView.findViewById(R.id.bookImage);  
    79.             hold.image.setImageDrawable(imageTask.loadImage(position, book.getCover(),imageCallback));  
    80.             hold.namtView = (TextView)convertView.findViewById(R.id.bookName);  
    81.             hold.idView = (TextView)convertView.findViewById(R.id.bookId);  
    82.             hold.introView = (TextView)convertView.findViewById(R.id.bookShortIntro);  
    83.             //保存标记  
    84.             convertView.setTag(hold);  
    85.         } else {  
    86.             hold = (ViewHold) convertView.getTag();  
    87.         }  
    88.           
    89.         /**获取数据,进行数据填充*/  
    90.           
    91.         // 标记图片视图,注意不能放在上面    
    92.         hold.image.setTag(position);  
    93.           
    94.         hold.image.setImageResource(R.drawable.ic_launcher);  
    95.         hold.namtView.setText(book.getTitle());   
    96.         hold.idView.setText(book.getId());  
    97.         hold.idView.setVisibility(View.GONE);  
    98.         hold.introView.setText(book.getShortIntro());  
    99.         itemMap.put(position+"", hold);  
    100.         return convertView;  
    101.     }  
    102.     /** 
    103.      * 屏幕停止滚动监听器 
    104.      */  
    105.       
    106.     AbsListView.OnScrollListener onScrollListener = new AbsListView.OnScrollListener() {    
    107.              
    108.          public void onScrollStateChanged(AbsListView view, int scrollState) {    
    109.              if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {  
    110.                  Log.i("--", "--");  
    111.                  int start_index = listView.getFirstVisiblePosition();  
    112.                  int end_index = listView.getLastVisiblePosition();  
    113.                    
    114.                  pageImgLoad(start_index,end_index);  
    115.              }    
    116.                  
    117.          }    
    118.              
    119.          public void onScroll(AbsListView view, int firstVisibleItem,    
    120.                  int visibleItemCount, int totalItemCount) {    
    121.              // TODO Auto-generated method stub    
    122.                  
    123.          }    
    124.     };  
    125.        
    126.     /** 
    127.       * 加载当前屏的图片 
    128.       */  
    129.     private void pageImgLoad(int start_index, int end_index) {    
    130.         for (; start_index < end_index; start_index++) {  
    131.             Book book = (Book) data.get(start_index).get(start_index+"");  
    132.             imageTask.loadImage(start_index, book.getCover(),imageCallback);  
    133.         }    
    134.     }  
    135.       
    136.     /**回调函数*/  
    137.     AsyncImageTask.ImageCallback imageCallback = new ImageCallback(){  
    138.         public void imageLoaded(Drawable image, int position) {  
    139.             if (image != null) {    
    140.                 //获取刚才标识的组件,并更新     
    141.                ImageView imageView = (ImageView) listView    
    142.                        .findViewWithTag(position);    
    143.                if (imageView != null) {  
    144.                    imageView.setImageDrawable(image);    
    145.                }    
    146.            }  
    147.         }  
    148.     };  
    149.       
    150.     @Override    
    151.     public int getCount() {    
    152.         return data.size();    
    153.     }    
    154.     
    155.     @Override    
    156.     public Object getItem(int position) {  
    157.         return itemMap.get(position+"");    
    158.     }    
    159.     
    160.     @Override    
    161.     public long getItemId(int position) {    
    162.         return position;    
    163.     }  
    164. }  



    [java] view plaincopy
     
      1. 到这里关键的都说完了.Activity那里那就贴代码了  
      2. 主要是什么时候实例MyListAdapter要注意一下  
      3. 如果你的是像搜结果显示在listView中的这种.  
      4. 那么MyListAdapter的数据源就要放在Button的点击响应事件里获取..然后通Message把数据源发送到handler中.在这handler中实例MyListAdapter对象.再绑定到listView.  
      5. 顺便说一下.另一种情况  
      6. 得到数据源.但ListView不显示内容.这是为什么 ?  
      7. 一般有两种原因.  
      8. 1, ListView不在handler中绑定数据..因为对组件的更新更改操作.一 定要在主线程中弄   
      9. 2.就是布局问题.你的ListView里的item不指定高度.----这个是最常见的..ListView的item一定要指定高度.  
      10. 就是你定义准备套在ListView中的那个layout_xxxx.xml这个文件中的LinearLayout这些要指定高度(最外面一层)<pre></pre>  
      11. <pre></pre>  
      12. <pre></pre>  
      13. <pre></pre>  
      14. <pre></pre>  
      15. <pre></pre>  
      16. <pre></pre>  
  • 相关阅读:
    使用通配符查询
    使用聚合函数查询
    有趣的图标
    利用Office Chart 制作柱图(一个柱子)
    众多select
    对象不能从 DBNull 转换为其他类型。
    使用谓词进行查询
    使用数学函数查询
    学生信息表 通过选择年级和班级得到详细的学生信息名单
    使用时间函数查询
  • 原文地址:https://www.cnblogs.com/tc310/p/4083152.html
Copyright © 2020-2023  润新知