一.简介
我们知道,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;
package com.liangdianshui.asynchronouslyload;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.ListView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends Activity {
private MyAdapter mAdapter;
private ListView mLv;
private String url = "http://www.imooc.com/api/teacher?type=4&num=30";
private final String TAG = MainActivity.class.getSimpleName();
private MyAsyncTask mAsyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
mAsyncTask = new MyAsyncTask();
mAsyncTask.execute(url);
}
private void initView() {
mLv = (ListView) findViewById(R.id.lv_main);
}
/**
* 异步加载数据
*/
class MyAsyncTask extends AsyncTask<String, Void, List<DataBean>> {
/**
* 异步加载数据
*
* @param params
* @return
*/
@Override
protected List<DataBean> doInBackground(String... params) {
return getJsonData(params[0]);
}
@Override
protected void onPostExecute(List<DataBean> dataBeans) {
super.onPostExecute(dataBeans);
mAdapter = new MyAdapter(MainActivity.this, dataBeans);
mLv.setAdapter(mAdapter);
}
}
/**
* 获取网络数据
*
* @param url
* @return
*/
private List<DataBean> getJsonData(String url) {
List<DataBean> list = new ArrayList<>();
JSONObject jsonObject;
DataBean dataBean;
try {
String strData = readStream(new URL(url).openStream());
if (strData != null) {
JSONObject allJsonObject = new JSONObject(strData);
JSONArray jsonArray = allJsonObject.getJSONArray("data");
for (int i = 0; i < jsonArray.length(); i++) {
jsonObject = jsonArray.getJSONObject(i);
Log.i(TAG, "jsonObject:" + jsonObject.toString());
dataBean = new DataBean();
dataBean.setDataIconUrl(jsonObject.getString("picSmall"));
dataBean.setDataTitle(jsonObject.getString("name"));
dataBean.setDataContent(jsonObject.getString("description"));
list.add(dataBean);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return list;
}
/**
* 读取数据流的信息
*
* @param inputStream
* @return
*/
private String readStream(InputStream inputStream) {
// String strData = null; //不能这样写
String strData = "";
InputStreamReader isReader = null;
BufferedReader bfReader = null;
try {
isReader = new InputStreamReader(inputStream, "utf-8");
bfReader = new BufferedReader(isReader);
// String line = null;
String line = "";
while ((line = bfReader.readLine()) != null) {
strData += line;
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
isReader.close();
bfReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return strData;
}
}
Bean对象:
package com.liangdianshui.asynchronouslyload;
/**
* Created by 两点水 on 2016/5/12.
*/
public class DataBean {
public String dataIconUrl;
public String dataTitle;
public String getDataContent() {
return dataContent;
}
public void setDataContent(String dataContent) {
this.dataContent = dataContent;
}
public String getDataTitle() {
return dataTitle;
}
public void setDataTitle(String dataTitle) {
this.dataTitle = dataTitle;
}
public String getDataIconUrl() {
return dataIconUrl;
}
public void setDataIconUrl(String dataIconUrl) {
this.dataIconUrl = dataIconUrl;
}
public String dataContent;
}
Adapter:
package com.liangdianshui.asynchronouslyload;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
/**
* Created by 两点水 on 2016/5/12.
*/
public class MyAdapter extends BaseAdapter {
private Context context;
private List<DataBean> list;
private ViewHolder mHolder;
public MyAdapter(Context context, List<DataBean> list) {
this.context = context;
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.item_layout, null);
mHolder = new ViewHolder();
mHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);
mHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);
mHolder.ivIcon= (ImageView) convertView.findViewById(R.id.iv_icon);
convertView.setTag(mHolder);
}else{
mHolder = (ViewHolder) convertView.getTag();
}
mHolder.tvTitle.setText(list.get(position).getDataTitle());
mHolder.tvContent.setText(list.get(position).getDataContent());
return convertView;
}
class ViewHolder {
private TextView tvTitle;
private TextView tvContent;
private ImageView ivIcon;
}
}
上面的实现之后,我们要把网络图片也显示到对应的item中的ImageView里!这里写了个类ImgeLoader用来异步加载网络图片,并显示到Imageview中!
package com.liangdianshui.asynchronouslyload;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.widget.ImageView;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Created by 两点水 on 2016/5/22.
*/
public class ImageLoader {
private ImageView mImageView;
private final int URL_BITMAP = 0;
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case URL_BITMAP:
mImageView.setImageBitmap((Bitmap) msg.obj);
break;
}
}
};
/**
* 多线程加载图片(Thread+Handler)
* 根据url获取的itmap显示在ImageView中
*
* @param imageView
* @param url
*/
public void showImageByThread(ImageView imageView, final String url) {
mImageView = imageView;
new Thread(new Runnable() {
@Override
public void run() {
Bitmap bitmap = getBitmapFromURL(url);
Message message = new Message();
message.obj = bitmap;
message.what = URL_BITMAP;
mHandler.sendMessage(message);
}
}).start(); //记得start();
}
/**
* 根据rul获取bitmap
*
* @param url
* @return
*/
private Bitmap getBitmapFromURL(String url) {
InputStream inputStream = null;
try {
URL urlBitmap = new URL(url);
HttpURLConnection urlConnection = (HttpURLConnection) urlBitmap.openConnection();
inputStream = new BufferedInputStream(urlConnection.getInputStream());
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
Thread.sleep(1000); //模拟网速慢的情况,为了更好的显示ImageView刷新多次的情况
urlConnection.disconnect(); //关闭http连接
return bitmap;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
try {
inputStream.close(); //关闭流
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
也在Adapter中调用这个方法并显示在ImageView中:
package com.liangdianshui.asynchronouslyload;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
/**
* Created by 两点水 on 2016/5/12.
*/
public class MyAdapter extends BaseAdapter {
private Context context;
private List<DataBean> list;
private ViewHolder mHolder;
public MyAdapter(Context context, List<DataBean> list) {
this.context = context;
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.item_layout, null);
mHolder = new ViewHolder();
mHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);
mHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);
mHolder.ivIcon= (ImageView) convertView.findViewById(R.id.iv_icon);
convertView.setTag(mHolder);
}else{
mHolder = (ViewHolder) convertView.getTag();
}
String url=list.get(position).getDataIconUrl();
new ImageLoader().showImageByThread(mHolder.ivIcon,url);
mHolder.tvTitle.setText(list.get(position).getDataTitle());
mHolder.tvContent.setText(list.get(position).getDataContent());
return convertView;
}
class ViewHolder {
private TextView tvTitle;
private TextView tvContent;
private ImageView ivIcon;
}
}
看下效果:
认真看下这个效果图,你会发现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内容的图片和重复刷新图片问题!
具体的代码:
package com.liangdianshui.asynchronouslyload;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
/**
* Created by 两点水 on 2016/5/12.
*/
public class MyAdapter extends BaseAdapter {
private Context context;
private List<DataBean> list;
private ViewHolder mHolder;
public MyAdapter(Context context, List<DataBean> list) {
this.context = context;
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.item_layout, null);
mHolder = new ViewHolder();
mHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);
mHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);
mHolder.ivIcon= (ImageView) convertView.findViewById(R.id.iv_icon);
convertView.setTag(mHolder);
}else{
mHolder = (ViewHolder) convertView.getTag();
}
String url=list.get(position).getDataIconUrl();
mHolder.ivIcon.setTag(url); //为防止listview显示的图片错乱,重复,闪烁
new ImageLoader().showImageByThread(mHolder.ivIcon,url);
mHolder.tvTitle.setText(list.get(position).getDataTitle());
mHolder.tvContent.setText(list.get(position).getDataContent());
return convertView;
}
class ViewHolder {
private TextView tvTitle;
private TextView tvContent;
private ImageView ivIcon;
}
}
ImageLoader中增加判断:
package com.liangdianshui.asynchronouslyload;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.widget.ImageView;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Created by 两点水 on 2016/5/22.
*/
public class ImageLoader {
private ImageView mImageView;
private String mUrl;
private final int URL_BITMAP = 0;
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case URL_BITMAP:
if (mImageView.getTag().equals(mUrl)){
mImageView.setImageBitmap((Bitmap) msg.obj);
}
break;
}
}
};
/**
* 多线程加载图片(Thread+Handler)
* 根据url获取的itmap显示在ImageView中
*
* @param imageView
* @param url
*/
public void showImageByThread(ImageView imageView, final String url) {
mImageView = imageView;
mUrl = url;
new Thread(new Runnable() {
@Override
public void run() {
Bitmap bitmap = getBitmapFromURL(url);
Message message = new Message();
message.obj = bitmap;
message.what = URL_BITMAP;
mHandler.sendMessage(message);
}
}).start(); //记得start();
}
/**
* 根据rul获取bitmap
*
* @param url
* @return
*/
private Bitmap getBitmapFromURL(String url) {
InputStream inputStream = null;
try {
URL urlBitmap = new URL(url);
HttpURLConnection urlConnection = (HttpURLConnection) urlBitmap.openConnection();
inputStream = new BufferedInputStream(urlConnection.getInputStream());
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
Thread.sleep(1000); //模拟网速慢的情况,为了更好的显示ImageView刷新多次的情况
urlConnection.disconnect(); //关闭http连接
return bitmap;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
try {
inputStream.close(); //关闭流
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
改完之后,看下效果图:
看效果图,你可能会怀疑是不是放错效果图了?怎么问题没有解决,跟上面的效果图差不多啊!其实不是的,上面的代码已经实现了ImageView显示不对和重复加载图片的问题,会出现上面的情况还是因为没有解决listView缓存的问题,我们在在Adapter里面Imageview显示异步加载图片的前面加上Imageview默认加载的图片的代码:mHolder.ivIcon.setImageResource(R.mipmap.ic_launcher);然后分别运行给ImageView加上标志(setTag)和不加标志的效果:
加上默认加载的图片和去掉tag的效果图
加上默认加载的图片和加上tag的效果图
细心的你会发现是有不同的!加上默认加载的图片和加上tag的效果图明显没有加载两次的效果!
加上默认加载的图片后,我们看到每次下拉或上拉都会先出现默认的图片(android机器人),这样也不好看,因此我们得加上缓存,给已经加载过的图片缓存起来,再次出现时,直接从缓存里拿,不用去下载!把已经显示过的图片缓存起来,可以增加用户的体验,减少流量!
加上缓存后的代码,主要是在Imageloader类上增加LruCache以及修改Adapter调用ImageLoader显示图片的方法切且在构造方法中初始化ImageLoader,目的是为了只创建一次缓存避免多次创建!
MyAdapter代码:
package com.liangdianshui.asynchronouslyload;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
/**
* Created by 两点水 on 2016/5/12.
*/
public class MyAdapter extends BaseAdapter {
private Context context;
private List<DataBean> list;
private ViewHolder mHolder;
private ImageLoader mImaeLoader;
public MyAdapter(Context context, List<DataBean> list) {
this.context = context;
this.list = list;
mImaeLoader = new ImageLoader(); //因为创建了缓存空间,所以放在构造方法,只创建一次
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.item_layout, null);
mHolder = new ViewHolder();
mHolder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);
mHolder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);
mHolder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);
convertView.setTag(mHolder);
} else {
mHolder = (ViewHolder) convertView.getTag();
}
String url = list.get(position).getDataIconUrl();
mHolder.ivIcon.setImageResource(R.mipmap.ic_launcher); //默认加载的图片
mHolder.ivIcon.setTag(url);//为防止listview显示的图片错乱,重复,闪烁
// new ImageLoader().showImageByThread(mHolder.ivIcon, url);
mImaeLoader.showImageByThread(mHolder.ivIcon, url);
mHolder.tvTitle.setText(list.get(position).getDataTitle());
mHolder.tvContent.setText(list.get(position).getDataContent());
return convertView;
}
class ViewHolder {
private TextView tvTitle;
private TextView tvContent;
private ImageView ivIcon;
}
}
ImageLoader的代码:
package com.liangdianshui.asynchronouslyload;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.util.LruCache;
import android.widget.ImageView;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Created by 两点水 on 2016/5/22.
*/
public class ImageLoader {
private ImageView mImageView;
private String mUrl;
private final int URL_BITMAP = 0;
private LruCache<String, Bitmap> mCache; //使用Lru缓存(使用的Lru算法:近期最少使用算法)
/**
* 使用缓存肯定要先声明缓存空间,因此在构造方法中声明缓存空间
*/
public ImageLoader() {
//获取程序最大使用的内存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 4; //缓存大小为最大缓存的四分之一
//创建缓存,把缓存大小传进去
mCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
//每次存入缓存的时候都会调用这个方法,因此我们把图片的大小放进去
return value.getByteCount();
}
};
}
/**
* 把图片加入到缓存
*
* @param url
* @param bitmap
*/
public void addBitmapToCache(String url, Bitmap bitmap) {
//LruCache好比是一个map,采用键值对的形式去保存图片
if (getBitmapFromCache(url) == null) {
//如果缓存中没有这个Bitmap,就把bitmap放进缓存
mCache.put(url, bitmap);
}
}
/**
* 从缓存中获取图片
*
* @param url
*/
public Bitmap getBitmapFromCache(String url) {
return mCache.get(url); //根据url获取图片
}
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case URL_BITMAP:
if (mImageView.getTag().equals(mUrl)) {
mImageView.setImageBitmap((Bitmap) msg.obj);
}
break;
}
}
};
/**
* 多线程加载图片(Thread+Handler)
* 根据url获取的itmap显示在ImageView中
*
* @param imageView
* @param url
*/
public void showImageByThread(ImageView imageView, final String url) {
mImageView = imageView;
mUrl = url;
//从缓存中取出图片
Bitmap bitmap = getBitmapFromCache(url);
//判断缓存中是否含有这张图片,没有的话去网络下载图片
if (bitmap == null) {
new Thread(new Runnable() {
@Override
public void run() {
Bitmap bitmap = getBitmapFromURL(url);
//把下载下来的图片加到缓存中
if (bitmap != null) {
addBitmapToCache(url, bitmap);
}
Message message = new Message();
message.obj = bitmap;
message.what = URL_BITMAP;
mHandler.sendMessage(message);
}
}).start(); //记得start();
} else {
Message message = new Message();
message.obj = bitmap;
message.what = URL_BITMAP;
mHandler.sendMessage(message);
}
}
/**
* 根据rul获取bitmap
*
* @param url
* @return
*/
private Bitmap getBitmapFromURL(String url) {
InputStream inputStream = null;
try {
URL urlBitmap = new URL(url);
HttpURLConnection urlConnection = (HttpURLConnection) urlBitmap.openConnection();
inputStream = new BufferedInputStream(urlConnection.getInputStream());
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
Thread.sleep(1000); //模拟网速慢的情况,为了更好的显示ImageView刷新多次的情况
urlConnection.disconnect(); //关闭http连接
return bitmap;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
try {
inputStream.close(); //关闭流
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
好了,我们来看下效果图!
看效果图后,我们发现效果并不是我们想象中的那样的!为什么呢?
我们仔细看效果图,可以发现,每个页面的最后一个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