handler添线程模式实现android应用的异步加载

handler加线程模式实现android应用的异步加载

           android开发经常需要访问加载网络图片,今天我做了一个笔记,一步一步由浅入深,来用handler+线程的模式来加载网络的图片,下面看看第一种情况:"每次创建一个线程来的模式"

        首先创建一个handler用来接收消息,为指定的imageView设置图片:

Handler handler = new Handler(){
		public void handleMessage(Message msg) {
			ImageView iv =  (ImageView) findViewById(msg.arg1);
			iv.setImageDrawable((Drawable) msg.obj);
			
		};
	};
         下面这个方法是加载指定url的图片,并且发送消息给handler

         private void loadImage(final String string, final int id) {
		// TODO Auto-generated method stub
		new Thread(){
			public void run() {
				try {
					Drawable drawable = Drawable.createFromStream(new URL(string).openStream(),"image.png");
					SystemClock.sleep(2000); //模拟访问网络
					Message msg = handler.obtainMessage();
					msg.arg1 = id;  //将imageView的id传送
					msg.obj = drawable;  //加载出的drawable实体对象
					handler.sendMessage(msg);
					
				} catch (MalformedURLException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
				
			};
		}.start();
	}
        在onCreate方法中,这样调用,为imageView来加载图片:

loadImage("http://192.168.0.8:8080/Struts2JQueryJson/test/one.png",R.id.imageOne);
loadImage("http://192.168.0.8:8080/Struts2JQueryJson/test/two.jpg",R.id.imageTwo);
loadImage("http://192.168.0.8:8080/Struts2JQueryJson/test/three.jpg",R.id.imageThree);
loadImage("http://192.168.0.8:8080/Struts2JQueryJson/test/four.png",R.id.imageFour);

         可以看到,我们每次调用一次loadImage方法都会新开启一个线程,这样做有一点坏处,就是当我们有很多图片需要加载的时候,是需要开启大量的线程来访问网络的,这样性能开销很大,所以我们接下来看看,利用线程池+handler的模式来访问网络图片,这样可以改善不断的开线程造成的性能开销很大的问题。


         线程池+handler

         这次,我们的loadImage方法会创建一个拥有四个线程的线程池,来加载网络图片:

       //新建一个拥有4个线程的线程池
	ExecutorService service = Executors.newFixedThreadPool(4);

	private void loadImage(final String string,final  int id) {
		// TODO Auto-generated method stub
		// serivce.submit(new Runnable() { }) 来确保下载是在线程池的线程中。
		service.submit(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				try {
					final Drawable drawable = Drawable.createFromStream(new URL(string).openStream(),"haha.png");
					//SystemClock.sleep(3000);//模拟网络访问网络的时间
					handler.post(new Runnable() {

						@Override
						public void run() {
							// TODO Auto-generated method stub
							ImageView iv = (ImageView) findViewById(id);
							iv.setImageDrawable(drawable);
						}
					});
				} catch (MalformedURLException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		});
          onCreate中的代码是不变的

//加载四张网络图片,将图片的url和索要显示图片的iamgeviewd的的id当做参数传入方法
loadImage("http://192.168.0.8:8080/Struts2JQueryJson/test/one.png",R.id.imageOne);
loadImage("http://192.168.0.8:8080/Struts2JQueryJson/test/two.jpg",R.id.imageTwo);
loadImage("http://192.168.0.8:8080/Struts2JQueryJson/test/three.jpg",R.id.imageThree);
loadImage("http://192.168.0.8:8080/Struts2JQueryJson/test/four.png",R.id.imageFour);
        可能,目前看不出和第一种的区别,这是因为我们现在加载的图片只有四张,如果我们加载的图片很多的话,区别还是比较明显的,但是仔细想想,这种方式还是有一点不足之处,那就是我们每次都需要访问网络来加载图片,如果我们能够将已经加载的图片来缓存起来,那是多么的美好啊,那么我们的第三种模式就应运而生了。

        线程池和内存缓存的模式:

首先新建一个AsyncImageLoader类,专门用来加载网络的图片的:

AsyncImageLoader.java

package com.example.sysnthreadpoolcache;

import java.io.IOException;
import java.lang.ref.SoftReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.widget.ImageView;

public class AsyncImageLoader {
	// 为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动)
	private Map<String,SoftReference<Drawable>>imageCache = new HashMap<String, SoftReference<Drawable>>();//定义缓存图片的map对象
	private ExecutorService service = Executors.newFixedThreadPool(4);//新建一个线程池,包含四个线程
	private Handler handler = new Handler();

	//对外部公开的接口
	public interface ImageCallback{
		// 注意 此方法是用来设置目标对象的图像资源
		public void imageLoaded(Drawable imageDrawable);
	}

	public Drawable loadDrawable(final String url,final ImageCallback callback){
		if (imageCache.containsKey(url)) {//如果缓存里边存在
			SoftReference<Drawable> drawableReference = imageCache.get(url);
			Drawable drawable =  drawableReference.get();
			if (null != drawable) {
				return drawable;
			}
		}

		service.submit(new Runnable() {//// 缓存中没有图像,则从网络上取出数据,并将取出的数据缓存到内存中
			@Override
			public void run() {
				// TODO Auto-generated method stub
				final Drawable drawable = loadImage(url);
				imageCache.put(url, new SoftReference<Drawable>(drawable));
				handler.post(new Runnable() {
					@Override
					public void run() {
						// TODO Auto-generated method stub
						callback.imageLoaded(drawable);
					}
				});
			}
		});
		return null;
	}
	private Drawable loadImage(final String url) {
		// TODO Auto-generated method stub
		Drawable drawable = null;
		
		try {
			drawable = Drawable.createFromStream(new URL(url).openStream(),"haha.png");
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return drawable;

	}
}
我们的MainActivity代码也很简单,如下:

public class MainActivity extends Activity {
	AsyncImageLoader loader = new AsyncImageLoader();
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		//加载四张网络图片,将图片的url和索要显示图片的iamgeviewd的的id当做参数传入方法
		loadImage("http://192.168.0.8:8080/Struts2JQueryJson/test/one.png",R.id.imageOne);
		loadImage("http://192.168.0.8:8080/Struts2JQueryJson/test/two.jpg",R.id.imageTwo);
		loadImage("http://192.168.0.8:8080/Struts2JQueryJson/test/three.jpg",R.id.imageThree);
		loadImage("http://192.168.0.8:8080/Struts2JQueryJson/test/four.png",R.id.imageFour);
	}

	private void loadImage(final String string, final int id) {
		// TODO Auto-generated method stub
		Drawable cacheImage = loader.loadDrawable(string, new ImageCallback() {

			@Override
			public void imageLoaded(Drawable imageDrawable) {
				// TODO Auto-generated method stub
				((ImageView) findViewById(id)).setImageDrawable(imageDrawable);
			}
		});
		if (cacheImage != null) {
			((ImageView) findViewById(id)).setImageDrawable(cacheImage);
		}
	}
}
        注意这里我搭建了一个本地服务器来存放网络上的图片,需要将其中的连接改为实际网络上的图片连接,通过Map<String,SoftReference<Drawable>>imageCache = new HashMap<String, SoftReference<Drawable>>();这种方式就实现了简单的内存缓存图片,首先访问并加载所需图片,断开网络,发现图片仍然可以显示,这就是因为我们已经将其缓存到内存中了,注意这种方式同样存在一个很大的缺陷,就是当内存吃紧的时候,系统会自动释放其认为没有用的内存,这样,我们的缓存效果就不是那么明显了,实际上在实际开发中,我们更多用的是lrucache和disklrucache来缓存图片的,这来那个种方法,以后会有,敬请期待。

    源码下载