zoukankan      html  css  js  c++  java
  • Android异步加载图像(含线程池,缓存方法)

    研究了android从网络上异步加载图像:

    (1)由于android UI更新支持单一线程原则,所以从网络上取数据并更新到界面上,为了不阻塞主线程首先可能会想到以下方法。

         在主线程中new 一个Handler对象,加载图像方法如下所示

    1. private void loadImage(final String url, final int id) {  
    2.         handler.post(new Runnable() {  
    3.                public void run() {  
    4.                    Drawable drawable = null;  
    5.                    try {  
    6.                        drawable = Drawable.createFromStream(new URL(url).openStream(), "image.png");  
    7.                    } catch (IOException e) {  
    8.                    }  
    9.                    ((ImageView) LazyLoadImageActivity.this.findViewById(id)).setImageDrawable(drawable);  
    10.                }  
    11.            });  
    12.    }  

    上面这个方法缺点很显然,经测试,如果要加载多个图片,这并不能实现异步加载,而是等到所有的图片都加载完才一起显示,因为它们都运行在一个线程中。

    然后,我们可以简单改进下,将Handler+Runnable模式改为Handler+Thread+Message模式不就能实现同时开启多个线程吗?

    (2)在主线程中new 一个Handler对象,代码如下:

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


    对应加载图像代码如下:对应加载图像代码如下:对应加载图像代码如下:

    1. // 引入线程池来管理多线程  
    2.    private void loadImage3(final String url, final int id) {  
    3.        executorService.submit(new Runnable() {  
    4.            public void run() {  
    5.                try {  
    6.                    final Drawable drawable = Drawable.createFromStream(new URL(url).openStream(), "image.png");  
    7.                    handler.post(new Runnable() {  
    8.   
    9.                        public void run() {  
    10.                            ((ImageView) LazyLoadImageActivity.this.findViewById(id)).setImageDrawable(drawable);  
    11.                        }  
    12.                    });  
    13.                } catch (Exception e) {  
    14.                    throw new RuntimeException(e);  
    15.                }  
    16.            }  
    17.        });  
    18.    }  

    (4)为了更方便使用我们可以将异步加载图像方法封装一个类,对外界只暴露一个方法即可,考虑到效率问题我们可以引入内存缓存机制,做法是

    建立一个HashMap,其键(key)为加载图像url,其值(value)是图像对象Drawable。先看一下我们封装的类

    1. public class AsyncImageLoader3 {  
    2.    //为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动)  
    3.     public Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();  
    4.     private ExecutorService executorService = Executors.newFixedThreadPool(5);    //固定五个线程来执行任务  
    5.     private final Handler handler=new Handler();  
    6.   
    7.      /** 
    8.      * 
    9.      * @param imageUrl     图像url地址 
    10.      * @param callback     回调接口 
    11.      * @return     返回内存中缓存的图像,第一次加载返回null 
    12.      */  
    13.     public Drawable loadDrawable(final String imageUrl, final ImageCallback callback) {  
    14.         //如果缓存过就从缓存中取出数据  
    15.         if (imageCache.containsKey(imageUrl)) {  
    16.             SoftReference<Drawable> softReference = imageCache.get(imageUrl);  
    17.             if (softReference.get() != null) {  
    18.                 return softReference.get();  
    19.             }  
    20.         }  
    21.         //缓存中没有图像,则从网络上取出数据,并将取出的数据缓存到内存中  
    22.          executorService.submit(new Runnable() {  
    23.             public void run() {  
    24.                 try {  
    25.                     final Drawable drawable = Drawable.createFromStream(new URL(imageUrl).openStream(), "image.png");  
    26.   
    27.                     imageCache.put(imageUrl, new SoftReference<Drawable>(drawable));  
    28.   
    29.                     handler.post(new Runnable() {  
    30.                         public void run() {  
    31.                            callback.imageLoaded(drawable);  
    32.                         }  
    33.                     });  
    34.                 } catch (Exception e) {  
    35.                     throw new RuntimeException(e);  
    36.                 }  
    37.             }  
    38.         });  
    39.         return null;  
    40.     }  
    41.      //从网络上取数据方法  
    42.     protected Drawable loadImageFromUrl(String imageUrl) {  
    43.         try {  
    44.             return Drawable.createFromStream(new URL(imageUrl).openStream(), "image.png");  
    45.         } catch (Exception e) {  
    46.             throw new RuntimeException(e);  
    47.         }  
    48.     }  
    49.     //对外界开放的回调接口  
    50.     public interface ImageCallback {  
    51.         //注意 此方法是用来设置目标对象的图像资源  
    52.         public void imageLoaded(Drawable imageDrawable);  
    53.     }  
    54. }  

    这样封装好后使用起来就方便多了。在主线程中首先要引入AsyncImageLoader3 对象,然后直接调用其loadDrawable方法即可,需要注意的是ImageCallback接口的imageLoaded方法是唯一可以把加载的图像设置到目标ImageView或其相关的组件上。

    在主线程调用代码:

      先实例化对象 private AsyncImageLoader3 asyncImageLoader3 = new AsyncImageLoader3();

      调用异步加载方法:

    1. //引入线程池,并引入内存缓存功能,并对外部调用封装了接口,简化调用过程  
    2.     private void loadImage4(final String url, final int id) {  
    3.           //如果缓存过就会从缓存中取出图像,ImageCallback接口中方法也不会被执行  
    4.          Drawable cacheImage = asyncImageLoader.loadDrawable(url,new AsyncImageLoader.ImageCallback() {  
    5.              //请参见实现:如果第一次加载url时下面方法会执行  
    6.              public void imageLoaded(Drawable imageDrawable) {  
    7.                ((ImageView) findViewById(id)).setImageDrawable(imageDrawable);  
    8.              }  
    9.          });  
    10.         if(cacheImage!=null){  
    11.           ((ImageView) findViewById(id)).setImageDrawable(cacheImage);  
    12.         }  
    13.     }  


    5)同理,下面也给出采用Thread+Handler+MessageQueue+内存缓存代码,原则同(4),只是把线程池换成了Thread+Handler+MessageQueue模式而已。代码如下:5)同理,下面也给出采用Thread+Handler+MessageQueue+内存缓存代码,原则同(4),只是把线程池换成了Thread+Handler+MessageQueue模式而已。代码如下:

    1. public class AsyncImageLoader {  
    2.    //为了加快速度,加入了缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动)  
    3.     private Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();  
    4.   
    5.      /** 
    6.      * 
    7.      * @param imageUrl     图像url地址 
    8.      * @param callback     回调接口 
    9.      * @return     返回内存中缓存的图像,第一次加载返回null 
    10.      */  
    11.     public Drawable loadDrawable(final String imageUrl, final ImageCallback callback) {  
    12.         //如果缓存过就从缓存中取出数据  
    13.         if (imageCache.containsKey(imageUrl)) {  
    14.             SoftReference<Drawable> softReference = imageCache.get(imageUrl);  
    15.             if (softReference.get() != null) {  
    16.                 return softReference.get();  
    17.             }  
    18.         }  
    19.   
    20.         final Handler handler = new Handler() {  
    21.             @Override  
    22.             public void handleMessage(Message msg) {  
    23.                 callback.imageLoaded((Drawable) msg.obj);  
    24.             }  
    25.         };  
    26.         new Thread() {  
    27.             public void run() {  
    28.                 Drawable drawable = loadImageFromUrl(imageUrl);  
    29.                 imageCache.put(imageUrl, new SoftReference<Drawable>(drawable));  
    30.                 handler.sendMessage(handler.obtainMessage(0, drawable));  
    31.   
    32.             }  
    33.   
    34.         }.start();  
    35.         /* 
    36.         下面注释的这段代码是Handler的一种代替方法 
    37.          */  
    38. //        new AsyncTask() {  
    39. //            @Override  
    40. //            protected Drawable doInBackground(Object... objects) {  
    41. //                  Drawable drawable = loadImageFromUrl(imageUrl);  
    42. //                imageCache.put(imageUrl, new SoftReference<Drawable>(drawable));  
    43. //                return  drawable;  
    44. //            }  
    45. //  
    46. //            @Override  
    47. //            protected void onPostExecute(Object o) {  
    48. //                  callback.imageLoaded((Drawable) o);  
    49. //            }  
    50. //        }.execute();  
    51.         return null;  
    52.     }  
    53.   
    54.     protected Drawable loadImageFromUrl(String imageUrl) {  
    55.         try {  
    56.             return Drawable.createFromStream(new URL(imageUrl).openStream(), "src");  
    57.         } catch (Exception e) {  
    58.             throw new RuntimeException(e);  
    59.         }  
    60.     }  
    61.     //对外界开放的回调接口  
    62.     public interface ImageCallback {  
    63.         public void imageLoaded(Drawable imageDrawable);  
    64.     }  
    65. }  


    至此,异步加载就介绍完了,下面给出的代码为测试用的完整代码:

    1. package com.bshark.supertelphone.activity;  
    2.   
    3. import android.app.Activity;  
    4. import android.graphics.drawable.Drawable;  
    5. import android.os.Bundle;  
    6. import android.os.Handler;  
    7. import android.os.Message;  
    8. import android.widget.ImageView;  
    9. import com.bshark.supertelphone.R;  
    10. import com.bshark.supertelphone.ui.adapter.util.AsyncImageLoader;  
    11. import com.bshark.supertelphone.ui.adapter.util.AsyncImageLoader3;  
    12.   
    13. import java.io.IOException;  
    14. import java.net.URL;  
    15. import java.util.concurrent.ExecutorService;  
    16. import java.util.concurrent.Executors;  
    17.   
    18. public class LazyLoadImageActivity extends Activity {  
    19.        final Handler handler=new Handler();  
    20.       final Handler handler2=new Handler(){  
    21.           @Override  
    22.           public void handleMessage(Message msg) {  
    23.              ((ImageView) LazyLoadImageActivity.this.findViewById(msg.arg1)).setImageDrawable((Drawable)msg.obj);  
    24.           }  
    25.       };  
    26. private ExecutorService executorService = Executors.newFixedThreadPool(5);    //固定五个线程来执行任务  
    27.     private AsyncImageLoader asyncImageLoader = new AsyncImageLoader();  
    28.     private AsyncImageLoader3 asyncImageLoader3 = new AsyncImageLoader3();  
    29.   
    30.   
    31. @Override  
    32. public void onCreate(Bundle savedInstanceState) {  
    33.   super.onCreate(savedInstanceState);  
    34.   setContentView(R.layout.main);  
    35.     
    36. //  loadImage("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.image1);  
    37. //  loadImage("http://www.baidu.com/img/baidu_logo.gif", R.id.image2);  
    38. //  loadImage("http://cache.soso.com/30d/img/web/logo.gif", R.id.image3);  
    39. //        loadImage("http://www.baidu.com/img/baidu_logo.gif", R.id.image4);  
    40. //  loadImage("http://cache.soso.com/30d/img/web/logo.gif", R.id.image5);  
    41.   
    42.         loadImage2("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.image1);  
    43.   loadImage2("http://www.baidu.com/img/baidu_logo.gif", R.id.image2);  
    44.   loadImage2("http://cache.soso.com/30d/img/web/logo.gif", R.id.image3);  
    45.         loadImage2("http://www.baidu.com/img/baidu_logo.gif", R.id.image4);  
    46.   loadImage2("http://cache.soso.com/30d/img/web/logo.gif", R.id.image5);  
    47. //        loadImage3("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.image1);  
    48. //  loadImage3("http://www.baidu.com/img/baidu_logo.gif", R.id.image2);  
    49. //  loadImage3("http://cache.soso.com/30d/img/web/logo.gif", R.id.image3);  
    50. //        loadImage3("http://www.baidu.com/img/baidu_logo.gif", R.id.image4);  
    51. //  loadImage3("http://cache.soso.com/30d/img/web/logo.gif", R.id.image5);  
    52.   
    53. //        loadImage4("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.image1);  
    54. //  loadImage4("http://www.baidu.com/img/baidu_logo.gif", R.id.image2);  
    55. //  loadImage4("http://cache.soso.com/30d/img/web/logo.gif", R.id.image3);  
    56. //        loadImage4("http://www.baidu.com/img/baidu_logo.gif", R.id.image4);  
    57. //  loadImage4("http://cache.soso.com/30d/img/web/logo.gif", R.id.image5);  
    58.   
    59. //        loadImage5("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.image1);  
    60. //        //为了测试缓存而模拟的网络延时  
    61. //        SystemClock.sleep(2000);  
    62. //  loadImage5("http://www.baidu.com/img/baidu_logo.gif", R.id.image2);  
    63. //        SystemClock.sleep(2000);  
    64. //  loadImage5("http://cache.soso.com/30d/img/web/logo.gif", R.id.image3);  
    65. //        SystemClock.sleep(2000);  
    66. //        loadImage5("http://www.baidu.com/img/baidu_logo.gif", R.id.image4);  
    67. //        SystemClock.sleep(2000);  
    68. //  loadImage5("http://cache.soso.com/30d/img/web/logo.gif", R.id.image5);  
    69. //        SystemClock.sleep(2000);  
    70. //         loadImage5("http://www.baidu.com/img/baidu_logo.gif", R.id.image4);  
    71. }  
    72.   
    73. @Override  
    74. protected void onDestroy() {  
    75.   executorService.shutdown();  
    76.   super.onDestroy();  
    77. }  
    78.     //线程加载图像基本原理  
    79.     private void loadImage(final String url, final int id) {  
    80.          handler.post(new Runnable() {  
    81.                 public void run() {  
    82.                     Drawable drawable = null;  
    83.                     try {  
    84.                         drawable = Drawable.createFromStream(new URL(url).openStream(), "image.png");  
    85.                     } catch (IOException e) {  
    86.                     }  
    87.                     ((ImageView) LazyLoadImageActivity.this.findViewById(id)).setImageDrawable(drawable);  
    88.                 }  
    89.             });  
    90.     }  
    91.      //采用handler+Thread模式实现多线程异步加载  
    92.      private void loadImage2(final String url, final int id) {  
    93.          Thread thread = new Thread(){  
    94.              @Override  
    95.              public void run() {  
    96.                Drawable drawable = null;  
    97.                     try {  
    98.                         drawable = Drawable.createFromStream(new URL(url).openStream(), "image.png");  
    99.                     } catch (IOException e) {  
    100.                     }  
    101.   
    102.                 Message message= handler2.obtainMessage() ;  
    103.                  message.arg1 = id;  
    104.                  message.obj = drawable;  
    105.                  handler2.sendMessage(message);  
    106.              }  
    107.          };  
    108.          thread.start();  
    109.          thread = null;  
    110.     }  
    111.     // 引入线程池来管理多线程  
    112.     private void loadImage3(final String url, final int id) {  
    113.         executorService.submit(new Runnable() {  
    114.             public void run() {  
    115.                 try {  
    116.                     final Drawable drawable = Drawable.createFromStream(new URL(url).openStream(), "image.png");  
    117.                     handler.post(new Runnable() {  
    118.   
    119.                         public void run() {  
    120.                             ((ImageView) LazyLoadImageActivity.this.findViewById(id)).setImageDrawable(drawable);  
    121.                         }  
    122.                     });  
    123.                 } catch (Exception e) {  
    124.                     throw new RuntimeException(e);  
    125.                 }  
    126.             }  
    127.         });  
    128.     }  
    129.     //引入线程池,并引入内存缓存功能,并对外部调用封装了接口,简化调用过程  
    130.     private void loadImage4(final String url, final int id) {  
    131.           //如果缓存过就会从缓存中取出图像,ImageCallback接口中方法也不会被执行  
    132.          Drawable cacheImage = asyncImageLoader.loadDrawable(url,new AsyncImageLoader.ImageCallback() {  
    133.              //请参见实现:如果第一次加载url时下面方法会执行  
    134.              public void imageLoaded(Drawable imageDrawable) {  
    135.                ((ImageView) findViewById(id)).setImageDrawable(imageDrawable);  
    136.              }  
    137.          });  
    138.         if(cacheImage!=null){  
    139.           ((ImageView) findViewById(id)).setImageDrawable(cacheImage);  
    140.         }  
    141.     }  
    142.   
    143.     //采用Handler+Thread+封装外部接口  
    144.     private void loadImage5(final String url, final int id) {  
    145.           //如果缓存过就会从缓存中取出图像,ImageCallback接口中方法也不会被执行  
    146.          Drawable cacheImage = asyncImageLoader3.loadDrawable(url,new AsyncImageLoader3.ImageCallback() {  
    147.              //请参见实现:如果第一次加载url时下面方法会执行  
    148.              public void imageLoaded(Drawable imageDrawable) {  
    149.                ((ImageView) findViewById(id)).setImageDrawable(imageDrawable);  
    150.              }  
    151.          });  
    152.         if(cacheImage!=null){  
    153.                     ((ImageView) findViewById(id)).setImageDrawable(cacheImage);  
    154.         }  
    155.     }  
    156.   
    157.   
    158. }  



    xml文件大致如下:

      1. <?xml version="1.0" encoding="utf-8"?>  
      2.   
      3. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
      4.               android:layout_width="fill_parent"  
      5.               android:orientation="vertical"  
      6.               android:layout_height="fill_parent" >  
      7.   <ImageView android:id="@+id/image1" android:layout_height="wrap_content" android:layout_width="fill_parent"></ImageView>  
      8.    <ImageView android:id="@+id/image2" android:layout_height="wrap_content" android:layout_width="fill_parent"></ImageView>  
      9.     <ImageView android:id="@+id/image3" android:layout_height="wrap_content" android:layout_width="fill_parent"></ImageView>  
      10.     <ImageView android:id="@+id/image5" android:layout_height="wrap_content" android:layout_width="fill_parent"></ImageView>  
      11.     <ImageView android:id="@+id/image4" android:layout_height="wrap_content" android:layout_width="fill_parent"></ImageView>  
      12. </LinearLayout>  
  • 相关阅读:
    扩展知识
    day61——多表操作(增、删除、改、基于对象的跨表查询)
    day60——单表操作补充(批量插入、查询、表结构)
    day59——orm单表操作
    day58——模板继承、组件、自定义标签和过滤器、inclusion_tag、静态文件配置、url别名和反向解析、url命名空间
    day57——视图、模板渲染
    WARNING: Ignoring invalid distribution -ip
    Python- 【python无法更新pip】提示python.exe: No module named pip
    Anaconda Prompt 切换路径不能进入D盘
    Failed calling sys.__interactivehook__ 错误的解决
  • 原文地址:https://www.cnblogs.com/wangfeng520/p/5030722.html
Copyright © 2011-2022 走看看