Android異步加載圖像小結 – Android移動開發技術文章_手機開發 Android移動開發教學課程

研究瞭android從網絡上異步加載圖像,現總結如下:


(1)由於android UI更新支持單一線程原則,所以從網絡上取數據並更新到界面上,為瞭不阻塞主線程首先可能會想到以下方法。


     在主線程中new 一個Handler對象,加載圖像方法如下所示


    private void loadImage(final String url, final int id) {
         handler.post(new Runnable() {
                public void run() {
                    Drawable drawable = null;
                    try {
                        drawable = Drawable.createFromStream(new URL(url).openStream(), “image.png”);
                    } catch (IOException e) {
                    }
                    ((ImageView) LazyLoadImageActivity.this.findViewById(id)).setImageDrawable(drawable);
                }
            });
    }


上面這個方法缺點很顯然,經測試,如果要加載多個圖片,這並不能實現異步加載,而是等到所有的圖片都加載完才一起顯示,因為它們都運行在一個線程中。


然後,我們可以簡單改進下,將Handler+Runnable模式改為Handler+Thread+Message模式不就能實現同時開啟多個線程嗎?


(2)在主線程中new 一個Handler對象,代碼如下:


    final Handler handler2=new Handler(){
          @Override
          public void handleMessage(Message msg) {
             ((ImageView) LazyLoadImageActivity.this.findViewById(msg.arg1)).setImageDrawable((Drawable)msg.obj);
          }
      };


    對應加載圖像代碼如下:


  //采用handler+Thread模式實現多線程異步加載
     private void loadImage2(final String url, final int id) {
         Thread thread = new Thread(){
             @Override
             public void run() {
               Drawable drawable = null;
                    try {
                        drawable = Drawable.createFromStream(new URL(url).openStream(), “image.png”);
                    } catch (IOException e) {
                    }


                Message message= handler2.obtainMessage() ;
                 message.arg1 = id;
                 message.obj = drawable;
                 handler2.sendMessage(message);
             }
         };
         thread.start();
         thread = null;
    }


這樣就簡單實現瞭異步加載瞭。細想一下,還可以優化的,比如引入線程池、引入緩存等,我們先介紹線程池。


(3)引入ExecutorService接口,於是代碼可以優化如下:


       在主線程中加入:private ExecutorService executorService = Executors.newFixedThreadPool(5);  


       對應加載圖像方法更改如下:


      // 引入線程池來管理多線程
    private void loadImage3(final String url, final int id) {
        executorService.submit(new Runnable() {
            public void run() {
                try {
                    final Drawable drawable = Drawable.createFromStream(new URL(url).openStream(), “image.png”);
                    handler.post(new Runnable() {


                        public void run() {
                            ((ImageView) LazyLoadImageActivity.this.findViewById(id)).setImageDrawable(drawable);
                        }
                    });
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }


(4)為瞭更方便使用我們可以將異步加載圖像方法封裝一個類,對外界隻暴露一個方法即可,考慮到效率問題我們可以引入內存緩存機制,做法是


建立一個HashMap,其鍵(key)為加載圖像url,其值(value)是圖像對象Drawable。先看一下我們封裝的類


public class AsyncImageLoader3 {
   //為瞭加快速度,在內存中開啟緩存(主要應用於重復圖片較多時,或者同一個圖片要多次被訪問,比如在ListView時來回滾動)
    public Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();
    private ExecutorService executorService = Executors.newFixedThreadPool(5);    //固定五個線程來執行任務
    private final Handler handler=new Handler();


     /**
     *
     * @param imageUrl     圖像url地址
     * @param callback     回調接口
     * @return     返回內存中緩存的圖像,第一次加載返回null
    

You May Also Like