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

jopen 10年前

           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来缓存图片的,这来那个种方法,以后会有,敬请期待。

     源码下载



来自: http://blog.csdn.net//mockingbirds/article/details/44814613