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;
- 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
 
                    
                

 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号