Android_异步加载1

一.简介
         我们知道,java中的线程是指异步,也就是在主程序进行的同时,副程序也在进行(比喻)。Java中,实现异步一般使用2种方法,一种是实现接口Runable,一种是继Thread。Android中,异步实现,一般用handler和AsyncTask,通常handler是一个消息栈,而AsyncTask不是。从Android 1.5开始系统将AsyncTask引入到android.os包中,过去在很早1.1和1.0 SDK时其实官方将其命名为UserTask,其内部是JDK 1.5开始新增的concurrent库,做过J2EE的网友可能明白并发库效率和强大性,比Java原始的Thread更灵活和强大,但对于轻量级的使用更为占用系统资源。Thread是Java早期为实现多线程而设计的,比较简单不支持concurrent中很多特性在同步和线程池类中需要自己去实现很多的东西,对于分布式应用来说更需要自己写调度代码,而为了Android UI的刷新Google引入了Handler和Looper机制,它们均基于消息实现,有事可能消息队列阻塞或其他原因无法准确的使用。Android开发网推荐大家使用AsyncTask代替Thread+Handler的方式,不仅调用上更为简单,经过实测更可靠一些,Google在Browser中大量使用了异步任务作为处理耗时的I/O操作,比如下载文件、读写数据库等等,它们在本质上都离不开消息,但是AsyncTask相比Thread加Handler更为可靠,更易于维护,但AsyncTask缺点也是有的比如一旦线程开启即dobackground方法执行后无法给线程发送消息,仅能通过预先设置好的标记来控制逻辑,当然可以通过线程的挂起等待标志位的改变来通讯,对于某些应用Thread和Handler以及Looper可能更灵活。
        异步加载是什么?怎么理解AsyncTask,这些简介在之前的博客中有介绍过:http://blog.csdn.net/two_water/article/details/51319192
二.Demo
        整个Demo的实现是通过网络读取Json数据,把相关信息显示到listview中,其中涉及到listview的优化,异步加载网络图片。
        Json数据的地址:http://www.imooc.com/api/teacher?type=4&num=30
        因为那个网址在后台设置了只可以读取30个数据,因此你会看到网址上有一百多条数据,可是显示到ListView中只能显示30条!
        首先来看看把Json数据中的“标题”和“简介”加载到listview中的效果图:

 

        代码就不全贴了,Demo在本文的最下方可以下载。在写这个代码的时候,有个地方没注意,readStream方法中把放回结果的字符串:String strData = null 这样写会让返回的数据前面有个null,还有注意一点就是string类型数据默认值是null;

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package com.liangdianshui.asynchronouslyload;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.AsyncTask;  
  5. import android.os.Bundle;  
  6. import android.util.Log;  
  7. import android.widget.ListView;  
  8.   
  9. import org.json.JSONArray;  
  10. import org.json.JSONException;  
  11. import org.json.JSONObject;  
  12.   
  13. import java.io.BufferedReader;  
  14. import java.io.IOException;  
  15. import java.io.InputStream;  
  16. import java.io.InputStreamReader;  
  17. import java.io.UnsupportedEncodingException;  
  18. import java.net.URL;  
  19. import java.util.ArrayList;  
  20. import java.util.List;  
  21.   
  22.   
  23. public class MainActivity extends Activity {  
  24.   
  25.     private MyAdapter mAdapter;  
  26.     private ListView mLv;  
  27.         private String url = "http://www.imooc.com/api/teacher?type=4&num=30";  
  28.     private final String TAG = MainActivity.class.getSimpleName();  
  29.     private MyAsyncTask mAsyncTask;  
  30.   
  31.     @Override  
  32.     protected void onCreate(Bundle savedInstanceState) {  
  33.         super.onCreate(savedInstanceState);  
  34.         setContentView(R.layout.activity_main);  
  35.         initView();  
  36.         mAsyncTask = new MyAsyncTask();  
  37.         mAsyncTask.execute(url);  
  38.     }  
  39.   
  40.     private void initView() {  
  41.         mLv = (ListView) findViewById(R.id.lv_main);  
  42.     }  
  43.   
  44.   
  45.     /** 
  46.      * 异步加载数据 
  47.      */  
  48.     class MyAsyncTask extends AsyncTask<String, Void, List<DataBean>> {  
  49.   
  50.         /** 
  51.          * 异步加载数据 
  52.          * 
  53.          * @param params 
  54.          * @return 
  55.          */  
  56.         @Override  
  57.         protected List<DataBean> doInBackground(String... params) {  
  58.             return getJsonData(params[0]);  
  59.         }  
  60.   
  61.         @Override  
  62.         protected void onPostExecute(List<DataBean> dataBeans) {  
  63.             super.onPostExecute(dataBeans);  
  64.             mAdapter = new MyAdapter(MainActivity.this, dataBeans);  
  65.             mLv.setAdapter(mAdapter);  
  66.         }  
  67.   
  68.     }  
  69.   
  70.     /** 
  71.      * 获取网络数据 
  72.      * 
  73.      * @param url 
  74.      * @return 
  75.      */  
  76.     private List<DataBean> getJsonData(String url) {  
  77.         List<DataBean> list = new ArrayList<>();  
  78.         JSONObject jsonObject;  
  79.         DataBean dataBean;  
  80.         try {  
  81.             String strData = readStream(new URL(url).openStream());  
  82.             if (strData != null) {  
  83.                 JSONObject allJsonObject = new JSONObject(strData);  
  84.                 JSONArray jsonArray = allJsonObject.getJSONArray("data");  
  85.                 for (int i = 0; i < jsonArray.length(); i++) {  
  86.                     jsonObject = jsonArray.getJSONObject(i);  
  87.                     Log.i(TAG, "jsonObject:" + jsonObject.toString());  
  88.                     dataBean = new DataBean();  
  89.                     dataBean.setDataIconUrl(jsonObject.getString("picSmall"));  
  90.                     dataBean.setDataTitle(jsonObject.getString("name"));  
  91.                     dataBean.setDataContent(jsonObject.getString("description"));  
  92.                     list.add(dataBean);  
  93.                 }  
  94.             }  
  95.         } catch (IOException e) {  
  96.             e.printStackTrace();  
  97.         } catch (JSONException e) {  
  98.             e.printStackTrace();  
  99.         }  
  100.         return list;  
  101.     }  
  102.   
  103.     /** 
  104.      * 读取数据流的信息 
  105.      * 
  106.      * @param inputStream 
  107.      * @return 
  108.      */  
  109.     private String readStream(InputStream inputStream) {  
  110. //      String strData = null;  //不能这样写  
  111.         String strData = "";  
  112.         InputStreamReader isReader = null;  
  113.         BufferedReader bfReader = null;  
  114.         try {  
  115.             isReader = new InputStreamReader(inputStream, "utf-8");  
  116.             bfReader = new BufferedReader(isReader);  
  117. //          String line = null;  
  118.             String line = "";  
  119.             while ((line = bfReader.readLine()) != null) {  
  120.                 strData += line;  
  121.             }  
  122.         } catch (UnsupportedEncodingException e) {  
  123.             e.printStackTrace();  
  124.         } catch (IOException e) {  
  125.             e.printStackTrace();  
  126.         } finally {  
  127.             try {  
  128.                 isReader.close();  
  129.                 bfReader.close();  
  130.             } catch (IOException e) {  
  131.                 e.printStackTrace();  
  132.             }  
  133.         }  
  134.         return strData;  
  135.     }  
  136.   
  137. }  

Bean对象:

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package com.liangdianshui.asynchronouslyload;  
  2.   
  3. /** 
  4.  * Created by 两点水 on 2016/5/12. 
  5.  */  
  6. public class DataBean {  
  7.   
  8.     public String dataIconUrl;  
  9.     public String dataTitle;  
  10.   
  11.     public String getDataContent() {  
  12.         return dataContent;  
  13.     }  
  14.   
  15.     public void setDataContent(String dataContent) {  
  16.         this.dataContent = dataContent;  
  17.     }  
  18.   
  19.     public String getDataTitle() {  
  20.         return dataTitle;  
  21.     }  
  22.   
  23.     public void setDataTitle(String dataTitle) {  
  24.         this.dataTitle = dataTitle;  
  25.     }  
  26.   
  27.     public String getDataIconUrl() {  
  28.         return dataIconUrl;  
  29.     }  
  30.   
  31.     public void setDataIconUrl(String dataIconUrl) {  
  32.         this.dataIconUrl = dataIconUrl;  
  33.     }  
  34.   
  35.     public String dataContent;  
  36. }  

Adapter:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package com.liangdianshui.asynchronouslyload;  
  2.   
  3. import android.content.Context;  
  4. import android.view.LayoutInflater;  
  5. import android.view.View;  
  6. import android.view.ViewGroup;  
  7. import android.widget.BaseAdapter;  
  8. import android.widget.ImageView;  
  9. import android.widget.TextView;  
  10.   
  11. import java.util.List;  
  12.   
  13. /** 
  14.  * Created by 两点水 on 2016/5/12. 
  15.  */  
  16. public class MyAdapter extends BaseAdapter {  
  17.   
  18.     private Context context;  
  19.     private List<DataBean> list;  
  20.     private ViewHolder mHolder;  
  21.   
  22.     public MyAdapter(Context context, List<DataBean> list) {  
  23.         this.context = context;  
  24.         this.list = list;  
  25.     }  
  26.   
  27.     @Override  
  28.     public int getCount() {  
  29.         return list.size();  
  30.     }  
  31.   
  32.     @Override  
  33.     public Object getItem(int position) {  
  34.         return list.get(position);  
  35.     }  
  36.   
  37.     @Override  
  38.     public long getItemId(int position) {  
  39.         return position;  
  40.     }  
  41.   
  42.     @Override  
  43.     public View getView(int position, View convertView, ViewGroup parent) {  
  44.         if (convertView == null) {  
  45.             convertView = LayoutInflater.from(context).inflate(R.layout.item_layout, null);  
  46.             mHolder = new ViewHolder();  
  47.             mHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);  
  48.             mHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);  
  49.             mHolder.ivIcon= (ImageView) convertView.findViewById(R.id.iv_icon);  
  50.             convertView.setTag(mHolder);  
  51.         }else{  
  52.             mHolder = (ViewHolder) convertView.getTag();  
  53.         }  
  54.         mHolder.tvTitle.setText(list.get(position).getDataTitle());  
  55.         mHolder.tvContent.setText(list.get(position).getDataContent());  
  56.         return convertView;  
  57.     }  
  58.   
  59.     class ViewHolder {  
  60.         private TextView tvTitle;  
  61.         private TextView tvContent;  
  62.         private ImageView ivIcon;  
  63.     }  
  64. }  

 


        上面的实现之后,我们要把网络图片也显示到对应的item中的ImageView里!这里写了个类ImgeLoader用来异步加载网络图片,并显示到Imageview中!

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package com.liangdianshui.asynchronouslyload;  
  2.   
  3. import android.graphics.Bitmap;  
  4. import android.graphics.BitmapFactory;  
  5. import android.os.Handler;  
  6. import android.os.Message;  
  7. import android.widget.ImageView;  
  8.   
  9. import java.io.BufferedInputStream;  
  10. import java.io.IOException;  
  11. import java.io.InputStream;  
  12. import java.net.HttpURLConnection;  
  13. import java.net.MalformedURLException;  
  14. import java.net.URL;  
  15.   
  16. /** 
  17.  * Created by 两点水 on 2016/5/22. 
  18.  */  
  19. public class ImageLoader {  
  20.   
  21.     private ImageView mImageView;  
  22.     private final int URL_BITMAP = 0;  
  23.   
  24.     Handler mHandler = new Handler() {  
  25.   
  26.         @Override  
  27.         public void handleMessage(Message msg) {  
  28.             super.handleMessage(msg);  
  29.             switch (msg.what) {  
  30.                 case URL_BITMAP:  
  31.                     mImageView.setImageBitmap((Bitmap) msg.obj);  
  32.                     break;  
  33.             }  
  34.         }  
  35.     };  
  36.   
  37.   
  38.     /** 
  39.      * 多线程加载图片(Thread+Handler) 
  40.      * 根据url获取的itmap显示在ImageView中 
  41.      * 
  42.      * @param imageView 
  43.      * @param url 
  44.      */  
  45.     public void showImageByThread(ImageView imageView, final String url) {  
  46.         mImageView = imageView;  
  47.   
  48.         new Thread(new Runnable() {  
  49.             @Override  
  50.             public void run() {  
  51.                 Bitmap bitmap = getBitmapFromURL(url);  
  52.                 Message message = new Message();  
  53.                 message.obj = bitmap;  
  54.                 message.what = URL_BITMAP;  
  55.                 mHandler.sendMessage(message);  
  56.             }  
  57.         }).start();  //记得start();  
  58.     }  
  59.   
  60.     /** 
  61.      * 根据rul获取bitmap 
  62.      * 
  63.      * @param url 
  64.      * @return 
  65.      */  
  66.     private Bitmap getBitmapFromURL(String url) {  
  67.         InputStream inputStream = null;  
  68.         try {  
  69.             URL urlBitmap = new URL(url);  
  70.             HttpURLConnection urlConnection = (HttpURLConnection) urlBitmap.openConnection();  
  71.             inputStream = new BufferedInputStream(urlConnection.getInputStream());  
  72.             Bitmap bitmap = BitmapFactory.decodeStream(inputStream);  
  73.             Thread.sleep(1000); //模拟网速慢的情况,为了更好的显示ImageView刷新多次的情况  
  74.             urlConnection.disconnect(); //关闭http连接  
  75.             return bitmap;  
  76.         } catch (MalformedURLException e) {  
  77.             e.printStackTrace();  
  78.         } catch (IOException e) {  
  79.             e.printStackTrace();  
  80.         } catch (InterruptedException e) {  
  81.             e.printStackTrace();  
  82.         } finally {  
  83.             try {  
  84.                 inputStream.close(); //关闭流  
  85.             } catch (IOException e) {  
  86.                 e.printStackTrace();  
  87.             }  
  88.         }  
  89.         return null;  
  90.     }  
  91.   
  92. }  

        也在Adapter中调用这个方法并显示在ImageView中:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package com.liangdianshui.asynchronouslyload;  
  2.   
  3. import android.content.Context;  
  4. import android.view.LayoutInflater;  
  5. import android.view.View;  
  6. import android.view.ViewGroup;  
  7. import android.widget.BaseAdapter;  
  8. import android.widget.ImageView;  
  9. import android.widget.TextView;  
  10.   
  11. import java.util.List;  
  12.   
  13. /** 
  14.  * Created by 两点水 on 2016/5/12. 
  15.  */  
  16. public class MyAdapter extends BaseAdapter {  
  17.   
  18.     private Context context;  
  19.     private List<DataBean> list;  
  20.     private ViewHolder mHolder;  
  21.   
  22.     public MyAdapter(Context context, List<DataBean> list) {  
  23.         this.context = context;  
  24.         this.list = list;  
  25.     }  
  26.   
  27.     @Override  
  28.     public int getCount() {  
  29.         return list.size();  
  30.     }  
  31.   
  32.     @Override  
  33.     public Object getItem(int position) {  
  34.         return list.get(position);  
  35.     }  
  36.   
  37.     @Override  
  38.     public long getItemId(int position) {  
  39.         return position;  
  40.     }  
  41.   
  42.     @Override  
  43.     public View getView(int position, View convertView, ViewGroup parent) {  
  44.         if (convertView == null) {  
  45.             convertView = LayoutInflater.from(context).inflate(R.layout.item_layout, null);  
  46.             mHolder = new ViewHolder();  
  47.             mHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);  
  48.             mHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);  
  49.             mHolder.ivIcon= (ImageView) convertView.findViewById(R.id.iv_icon);  
  50.             convertView.setTag(mHolder);  
  51.         }else{  
  52.             mHolder = (ViewHolder) convertView.getTag();  
  53.         }  
  54.         String url=list.get(position).getDataIconUrl();  
  55.         new ImageLoader().showImageByThread(mHolder.ivIcon,url);  
  56.         mHolder.tvTitle.setText(list.get(position).getDataTitle());  
  57.         mHolder.tvContent.setText(list.get(position).getDataContent());  
  58.         return convertView;  
  59.     }  
  60.   
  61.     class ViewHolder {  
  62.         private TextView tvTitle;  
  63.         private TextView tvContent;  
  64.         private ImageView ivIcon;  
  65.     }  
  66. }  

 

        看下效果:

 

 

 

 

       

 认真看下这个效果图,你会发现ImageView 会刷新多次,为什么呢?
        这是因为listView有缓存机制,ListView会重用已显示过的布局,因此ImageView首先显示的是重用convertView布局中的图片,然后再根据URL更新图片,因此会出现ImageView刷新多次的问题!
        总的来说,我们为了使得性能更优,使用了ListView item缓存机制,给我们带来了以下的优点:
        1.ListView会缓存行item(某行对应的View)。ListView通过adapter的getView函数获得每行的item。
        2.在滑动过程中,会出现a: 如果某行item已经滑出屏幕,若该item不在缓存内,则put进缓存,否则更新缓存; 获取滑入屏幕的行item之前会先判断缓存中是否有可用的item,如果有,做为convertView参数传递给adapter的getView。
        但也正因此,也会给我们异步加载图片带来了一些问题:
        1.行item图片显示重复
        这个显示重复是指当前行item显示了之前某行item的图片。
        比如ListView滑动到第2行会异步加载某个图片,但是加载很慢,加载过程中listView已经滑动到了第14行,且滑动过程中该图片加载结束,第2行已不在屏幕内,根据上面介绍的缓存原理,第2行的view可能被第14行复用,这样我们看到的就是第14行显示了本该属于第2行的图片,造成显示重复。
        2. 行item图片显示错乱
        这个显示错乱是指某行item显示了不属于该行item的图片。
        比如ListView滑动到第2行会异步加载某个图片,但是加载很慢,加载过程中listView已经滑动到了第14行,第2行已不在屏幕内,根据上面介绍的缓存原理,第2行的view可能被第14行复用,第14行显示了第2行的View,这时之前的图片加载结束,就会显示在第14行,造成错乱。
        3. 行item图片显示闪烁
        根据上面的情况,第14行图片又很快加载结束,所以我们看到第14行先显示了第2行的图片,立马又显示了自己的图片进行覆盖造成闪烁错乱。
        解决方案:
        通过上面的分析,我们可以知道,如果每次getView能给对象一个标识,在异步加载完成时比较标识与当前行item的标识是否一致,一致则显示,否则不做处理即可。简单来说就是我们把每个ImageView设置一个唯一的标志,每次异步加载完图片,在ImageView显示的时候,判断是否标志一样,如果一样才在ImageView中显示,因此就不出现ImageView中显示不对应item内容的图片和重复刷新图片问题!
具体的代码:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package com.liangdianshui.asynchronouslyload;  
  2.   
  3. import android.content.Context;  
  4. import android.view.LayoutInflater;  
  5. import android.view.View;  
  6. import android.view.ViewGroup;  
  7. import android.widget.BaseAdapter;  
  8. import android.widget.ImageView;  
  9. import android.widget.TextView;  
  10.   
  11. import java.util.List;  
  12.   
  13. /** 
  14.  * Created by 两点水 on 2016/5/12. 
  15.  */  
  16. public class MyAdapter extends BaseAdapter {  
  17.   
  18.     private Context context;  
  19.     private List<DataBean> list;  
  20.     private ViewHolder mHolder;  
  21.   
  22.     public MyAdapter(Context context, List<DataBean> list) {  
  23.         this.context = context;  
  24.         this.list = list;  
  25.     }  
  26.   
  27.     @Override  
  28.     public int getCount() {  
  29.         return list.size();  
  30.     }  
  31.   
  32.     @Override  
  33.     public Object getItem(int position) {  
  34.         return list.get(position);  
  35.     }  
  36.   
  37.     @Override  
  38.     public long getItemId(int position) {  
  39.         return position;  
  40.     }  
  41.   
  42.     @Override  
  43.     public View getView(int position, View convertView, ViewGroup parent) {  
  44.         if (convertView == null) {  
  45.             convertView = LayoutInflater.from(context).inflate(R.layout.item_layout, null);  
  46.             mHolder = new ViewHolder();  
  47.             mHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);  
  48.             mHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);  
  49.             mHolder.ivIcon= (ImageView) convertView.findViewById(R.id.iv_icon);  
  50.             convertView.setTag(mHolder);  
  51.         }else{  
  52.             mHolder = (ViewHolder) convertView.getTag();  
  53.         }  
  54.         String url=list.get(position).getDataIconUrl();  
  55.         mHolder.ivIcon.setTag(url);   //为防止listview显示的图片错乱,重复,闪烁  
  56.         new ImageLoader().showImageByThread(mHolder.ivIcon,url);  
  57.         mHolder.tvTitle.setText(list.get(position).getDataTitle());  
  58.         mHolder.tvContent.setText(list.get(position).getDataContent());  
  59.         return convertView;  
  60.     }  
  61.   
  62.     class ViewHolder {  
  63.         private TextView tvTitle;  
  64.         private TextView tvContent;  
  65.         private ImageView ivIcon;  
  66.     }  
  67. }  

         ImageLoader中增加判断:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package com.liangdianshui.asynchronouslyload;  
  2.   
  3. import android.graphics.Bitmap;  
  4. import android.graphics.BitmapFactory;  
  5. import android.os.Handler;  
  6. import android.os.Message;  
  7. import android.widget.ImageView;  
  8.   
  9. import java.io.BufferedInputStream;  
  10. import java.io.IOException;  
  11. import java.io.InputStream;  
  12. import java.net.HttpURLConnection;  
  13. import java.net.MalformedURLException;  
  14. import java.net.URL;  
  15.   
  16. /** 
  17.  * Created by 两点水 on 2016/5/22. 
  18.  */  
  19. public class ImageLoader {  
  20.   
  21.     private ImageView mImageView;  
  22.     private String mUrl;  
  23.     private final int URL_BITMAP = 0;  
  24.   
  25.     Handler mHandler = new Handler() {  
  26.   
  27.         @Override  
  28.         public void handleMessage(Message msg) {  
  29.             super.handleMessage(msg);  
  30.             switch (msg.what) {  
  31.                 case URL_BITMAP:  
  32.                     if (mImageView.getTag().equals(mUrl)){  
  33.                         mImageView.setImageBitmap((Bitmap) msg.obj);  
  34.                     }  
  35.                     break;  
  36.             }  
  37.         }  
  38.     };  
  39.   
  40.   
  41.     /** 
  42.      * 多线程加载图片(Thread+Handler) 
  43.      * 根据url获取的itmap显示在ImageView中 
  44.      * 
  45.      * @param imageView 
  46.      * @param url 
  47.      */  
  48.     public void showImageByThread(ImageView imageView, final String url) {  
  49.         mImageView = imageView;  
  50.         mUrl = url;  
  51.   
  52.         new Thread(new Runnable() {  
  53.             @Override  
  54.             public void run() {  
  55.                 Bitmap bitmap = getBitmapFromURL(url);  
  56.                 Message message = new Message();  
  57.                 message.obj = bitmap;  
  58.                 message.what = URL_BITMAP;  
  59.                 mHandler.sendMessage(message);  
  60.             }  
  61.         }).start();  //记得start();  
  62.     }  
  63.   
  64.     /** 
  65.      * 根据rul获取bitmap 
  66.      * 
  67.      * @param url 
  68.      * @return 
  69.      */  
  70.     private Bitmap getBitmapFromURL(String url) {  
  71.         InputStream inputStream = null;  
  72.         try {  
  73.             URL urlBitmap = new URL(url);  
  74.             HttpURLConnection urlConnection = (HttpURLConnection) urlBitmap.openConnection();  
  75.             inputStream = new BufferedInputStream(urlConnection.getInputStream());  
  76.             Bitmap bitmap = BitmapFactory.decodeStream(inputStream);  
  77.             Thread.sleep(1000); //模拟网速慢的情况,为了更好的显示ImageView刷新多次的情况  
  78.             urlConnection.disconnect(); //关闭http连接  
  79.             return bitmap;  
  80.         } catch (MalformedURLException e) {  
  81.             e.printStackTrace();  
  82.         } catch (IOException e) {  
  83.             e.printStackTrace();  
  84.         } catch (InterruptedException e) {  
  85.             e.printStackTrace();  
  86.         } finally {  
  87.             try {  
  88.                 inputStream.close(); //关闭流  
  89.             } catch (IOException e) {  
  90.                 e.printStackTrace();  
  91.             }  
  92.         }  
  93.         return null;  
  94.     }  
  95.   
  96. }  

        改完之后,看下效果图:

      

           看效果图,你可能会怀疑是不是放错效果图了?怎么问题没有解决,跟上面的效果图差不多啊!其实不是的,上面的代码已经实现了ImageView显示不对和重复加载图片的问题,会出现上面的情况还是因为没有解决listView缓存的问题,我们在在Adapter里面Imageview显示异步加载图片的前面加上Imageview默认加载的图片的代码:mHolder.ivIcon.setImageResource(R.mipmap.ic_launcher);然后分别运行给ImageView加上标志(setTag)和不加标志的效果:

     

加上默认加载的图片和去掉tag的效果图     

  

 加上默认加载的图片和加上tag的效果图  

        细心的你会发现是有不同的!加上默认加载的图片和加上tag的效果图明显没有加载两次的效果!
       加上默认加载的图片后,我们看到每次下拉或上拉都会先出现默认的图片(Android机器人),这样也不好看,因此我们得加上缓存,给已经加载过的图片缓存起来,再次出现时,直接从缓存里拿,不用去下载!把已经显示过的图片缓存起来,可以增加用户的体验,减少流量!
      加上缓存后的代码,主要是在Imageloader类上增加LruCache以及修改Adapter调用ImageLoader显示图片的方法切且在构造方法中初始化ImageLoader,目的是为了只创建一次缓存避免多次创建!
MyAdapter代码:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package com.liangdianshui.asynchronouslyload;  
  2.   
  3. import android.content.Context;  
  4. import android.view.LayoutInflater;  
  5. import android.view.View;  
  6. import android.view.ViewGroup;  
  7. import android.widget.BaseAdapter;  
  8. import android.widget.ImageView;  
  9. import android.widget.TextView;  
  10.   
  11. import java.util.List;  
  12.   
  13. /** 
  14.  * Created by 两点水 on 2016/5/12. 
  15.  */  
  16. public class MyAdapter extends BaseAdapter {  
  17.   
  18.     private Context context;  
  19.     private List<DataBean> list;  
  20.     private ViewHolder mHolder;  
  21.     private ImageLoader mImaeLoader;  
  22.   
  23.     public MyAdapter(Context context, List<DataBean> list) {  
  24.         this.context = context;  
  25.         this.list = list;  
  26.         mImaeLoader = new ImageLoader();  //因为创建了缓存空间,所以放在构造方法,只创建一次  
  27.     }  
  28.   
  29.     @Override  
  30.     public int getCount() {  
  31.         return list.size();  
  32.     }  
  33.   
  34.     @Override  
  35.     public Object getItem(int position) {  
  36.         return list.get(position);  
  37.     }  
  38.   
  39.     @Override  
  40.     public long getItemId(int position) {  
  41.         return position;  
  42.     }  
  43.   
  44.     @Override  
  45.     public View getView(int position, View convertView, ViewGroup parent) {  
  46.         if (convertView == null) {  
  47.             convertView = LayoutInflater.from(context).inflate(R.layout.item_layout, null);  
  48.             mHolder = new ViewHolder();  
  49.             mHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);  
  50.             mHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);  
  51.             mHolder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);  
  52.             convertView.setTag(mHolder);  
  53.         } else {  
  54.             mHolder = (ViewHolder) convertView.getTag();  
  55.         }  
  56.         String url = list.get(position).getDataIconUrl();  
  57.         mHolder.ivIcon.setImageResource(R.mipmap.ic_launcher); //默认加载的图片  
  58.         mHolder.ivIcon.setTag(url);//为防止listview显示的图片错乱,重复,闪烁  
  59. //      new ImageLoader().showImageByThread(mHolder.ivIcon, url);  
  60.         mImaeLoader.showImageByThread(mHolder.ivIcon, url);  
  61.         mHolder.tvTitle.setText(list.get(position).getDataTitle());  
  62.         mHolder.tvContent.setText(list.get(position).getDataContent());  
  63.         return convertView;  
  64.     }  
  65.   
  66.     class ViewHolder {  
  67.         private TextView tvTitle;  
  68.         private TextView tvContent;  
  69.         private ImageView ivIcon;  
  70.     }  
  71. }  

 

ImageLoader的代码:

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. package com.liangdianshui.asynchronouslyload;  
  2.   
  3. import android.graphics.Bitmap;  
  4. import android.graphics.BitmapFactory;  
  5. import android.os.Handler;  
  6. import android.os.Message;  
  7. import android.util.Log;  
  8. import android.util.LruCache;  
  9. import android.widget.ImageView;  
  10.   
  11. import java.io.BufferedInputStream;  
  12. import java.io.IOException;  
  13. import java.io.InputStream;  
  14. import java.net.HttpURLConnection;  
  15. import java.net.MalformedURLException;  
  16. import java.net.URL;  
  17.   
  18. /** 
  19.  * Created by 两点水 on 2016/5/22. 
  20.  */  
  21. public class ImageLoader {  
  22.   
  23.     private ImageView mImageView;  
  24.     private String mUrl;  
  25.     private final int URL_BITMAP = 0;  
  26.     private LruCache<String, Bitmap> mCache;   //使用Lru缓存(使用的Lru算法:近期最少使用算法)  
  27.   
  28.     /** 
  29.      * 使用缓存肯定要先声明缓存空间,因此在构造方法中声明缓存空间 
  30.      */  
  31.     public ImageLoader() {  
  32.         //获取程序最大使用的内存  
  33.         int maxMemory = (int) Runtime.getRuntime().maxMemory();  
  34.         int cacheSize = maxMemory / 4;  //缓存大小为最大缓存的四分之一  
  35.         //创建缓存,把缓存大小传进去  
  36.         mCache = new LruCache<String, Bitmap>(cacheSize) {  
  37.             @Override  
  38.             protected int sizeOf(String key, Bitmap value) {  
  39.                 //每次存入缓存的时候都会调用这个方法,因此我们把图片的大小放进去  
  40.                 return value.getByteCount();  
  41.             }  
  42.         };  
  43.     }  
  44.   
  45.     /** 
  46.      * 把图片加入到缓存 
  47.      * 
  48.      * @param url 
  49.      * @param bitmap 
  50.      */  
  51.     public void addBitmapToCache(String url, Bitmap bitmap) {  
  52.         //LruCache好比是一个map,采用键值对的形式去保存图片  
  53.         if (getBitmapFromCache(url) == null) {  
  54.             //如果缓存中没有这个Bitmap,就把bitmap放进缓存  
  55.             mCache.put(url, bitmap);  
  56.         }  
  57.     }  
  58.   
  59.     /** 
  60.      * 从缓存中获取图片 
  61.      * 
  62.      * @param url 
  63.      */  
  64.     public Bitmap getBitmapFromCache(String url) {  
  65.         return mCache.get(url); //根据url获取图片  
  66.     }  
  67.   
  68.     Handler mHandler = new Handler() {  
  69.   
  70.         @Override  
  71.         public void handleMessage(Message msg) {  
  72.             super.handleMessage(msg);  
  73.             switch (msg.what) {  
  74.                 case URL_BITMAP:  
  75.                     if (mImageView.getTag().equals(mUrl)) {  
  76.                         mImageView.setImageBitmap((Bitmap) msg.obj);  
  77.                     }  
  78.                     break;  
  79.             }  
  80.         }  
  81.     };  
  82.   
  83.   
  84.     /** 
  85.      * 多线程加载图片(Thread+Handler) 
  86.      * 根据url获取的itmap显示在ImageView中 
  87.      * 
  88.      * @param imageView 
  89.      * @param url 
  90.      */  
  91.     public void showImageByThread(ImageView imageView, final String url) {  
  92.         mImageView = imageView;  
  93.         mUrl = url;  
  94.   
  95.         //从缓存中取出图片  
  96.         Bitmap bitmap = getBitmapFromCache(url);  
  97.         //判断缓存中是否含有这张图片,没有的话去网络下载图片  
  98.         if (bitmap == null) {  
  99.             new Thread(new Runnable() {  
  100.                 @Override  
  101.                 public void run() {  
  102.                     Bitmap bitmap = getBitmapFromURL(url);  
  103.                     //把下载下来的图片加到缓存中  
  104.                     if (bitmap != null) {  
  105.                         addBitmapToCache(url, bitmap);  
  106.                     }  
  107.                     Message message = new Message();  
  108.                     message.obj = bitmap;  
  109.                     message.what = URL_BITMAP;  
  110.                     mHandler.sendMessage(message);  
  111.                 }  
  112.             }).start();  //记得start();  
  113.         } else {  
  114.             Message message = new Message();  
  115.             message.obj = bitmap;  
  116.             message.what = URL_BITMAP;  
  117.             mHandler.sendMessage(message);  
  118.         }  
  119.     }  
  120.   
  121.     /** 
  122.      * 根据rul获取bitmap 
  123.      * 
  124.      * @param url 
  125.      * @return 
  126.      */  
  127.   
  128.     private Bitmap getBitmapFromURL(String url) {  
  129.         InputStream inputStream = null;  
  130.         try {  
  131.             URL urlBitmap = new URL(url);  
  132.             HttpURLConnection urlConnection = (HttpURLConnection) urlBitmap.openConnection();  
  133.             inputStream = new BufferedInputStream(urlConnection.getInputStream());  
  134.             Bitmap bitmap = BitmapFactory.decodeStream(inputStream);  
  135.             Thread.sleep(1000); //模拟网速慢的情况,为了更好的显示ImageView刷新多次的情况  
  136.             urlConnection.disconnect(); //关闭http连接  
  137.             return bitmap;  
  138.         } catch (MalformedURLException e) {  
  139.             e.printStackTrace();  
  140.         } catch (IOException e) {  
  141.             e.printStackTrace();  
  142.         } catch (InterruptedException e) {  
  143.             e.printStackTrace();  
  144.         } finally {  
  145.             try {  
  146.                 inputStream.close(); //关闭流  
  147.             } catch (IOException e) {  
  148.                 e.printStackTrace();  
  149.             }  
  150.         }  
  151.         return null;  
  152.     }  
  153.   
  154. }  

         好了,我们来看下效果图!

 

 

 

          看效果图后,我们发现效果并不是我们想象中的那样的!为什么呢?
       我们仔细看效果图,可以发现,每个页面的最后一个Item才刷新了,而且是刷新了很多次,我们可以猜测是所有下载的图片都显示到了最后的那个Item那里了!为什么呢?我们看回我们之前的代码和通过打log可以发现,我们是没有传错数值的!看代码我们可以知道,图片下载完毕后,通过handler将图片通知UI线程修改ImageView,但是这个时候,参数中的ImageView与这时候传递过来的图像可能并不是一一对应的关系,因为参数中的ImageView刷新的很快,一下子到最后一个item的ImageView了,所以显示肯定不正确了,也就会出现我们所看到的只在最后一个ImageView更新图片的现象了,因此我们还需要让url和对应的ImageView进行下配对,最简单的方法就是使用一个对象来进行保存。

       因为一遍博客的字数有限制,Demo的下载地址和接下来问题的解决就在下一篇博客记录了,下一篇博客还会记录AsyncTask实现异步加载图片,还有ListView滚动时的加载优化和图片的加载优化!

       博客地址:http://blog.csdn.net/Two_Water/article/details/51550489

posted @ 2017-04-23 22:07  天涯海角路  阅读(114)  评论(0)    收藏  举报