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中的效果图:

Android_异步加载1_第1张图片

        代码就不全贴了,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> {

		/**
		 * 异步加载数据
		 *
		 * @param params
		 * @return
		 */
		@Override
		protected List doInBackground(String... params) {
			return getJsonData(params[0]);
		}

		@Override
		protected void onPostExecute(List dataBeans) {
			super.onPostExecute(dataBeans);
			mAdapter = new MyAdapter(MainActivity.this, dataBeans);
			mLv.setAdapter(mAdapter);
		}

	}

	/**
	 * 获取网络数据
	 *
	 * @param url
	 * @return
	 */
	private List getJsonData(String url) {
		List 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 list;
	private ViewHolder mHolder;

	public MyAdapter(Context context, List 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 list;
	private ViewHolder mHolder;

	public MyAdapter(Context context, List 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;
	}
}

        看下效果:

Android_异步加载1_第2张图片

       

 认真看下这个效果图,你会发现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 list;
	private ViewHolder mHolder;

	public MyAdapter(Context context, List 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)和不加标志的效果:

   Android_异步加载1_第3张图片  

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

  Android_异步加载1_第4张图片

 加上默认加载的图片和加上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 list;
    private ViewHolder mHolder;
    private ImageLoader mImaeLoader;

    public MyAdapter(Context context, List 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 mCache;   //使用Lru缓存(使用的Lru算法:近期最少使用算法)

	/**
	 * 使用缓存肯定要先声明缓存空间,因此在构造方法中声明缓存空间
	 */
	public ImageLoader() {
		//获取程序最大使用的内存
		int maxMemory = (int) Runtime.getRuntime().maxMemory();
		int cacheSize = maxMemory / 4;  //缓存大小为最大缓存的四分之一
		//创建缓存,把缓存大小传进去
		mCache = new LruCache(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;
	}

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

Android_异步加载1_第5张图片

          看效果图后,我们发现效果并不是我们想象中的那样的!为什么呢?
       我们仔细看效果图,可以发现,每个页面的最后一个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











你可能感兴趣的:(【Android】,水【Android基础】,android,异步)