Android异步加载图像(含线程池,缓存方法)

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
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对象,代码如下:

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


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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 引入线程池来管理多线程
    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。先看一下我们封装的类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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
      */
     public Drawable loadDrawable( final String imageUrl,  final ImageCallback callback) {
         //如果缓存过就从缓存中取出数据
         if (imageCache.containsKey(imageUrl)) {
             SoftReference<Drawable> softReference = imageCache.get(imageUrl);
             if (softReference.get() !=  null ) {
                 return softReference.get();
             }
         }
         //缓存中没有图像,则从网络上取出数据,并将取出的数据缓存到内存中
          executorService.submit( new Runnable() {
             public void run() {
                 try {
                     final Drawable drawable = Drawable.createFromStream( new URL(imageUrl).openStream(),  "image.png" );
 
                     imageCache.put(imageUrl,  new SoftReference<Drawable>(drawable));
 
                     handler.post( new Runnable() {
                         public void run() {
                            callback.imageLoaded(drawable);
                         }
                     });
                 catch (Exception e) {
                     throw new RuntimeException(e);
                 }
             }
         });
         return null ;
     }
      //从网络上取数据方法
     protected Drawable loadImageFromUrl(String imageUrl) {
         try {
             return Drawable.createFromStream( new URL(imageUrl).openStream(),  "image.png" );
         catch (Exception e) {
             throw new RuntimeException(e);
         }
     }
     //对外界开放的回调接口
     public interface ImageCallback {
         //注意 此方法是用来设置目标对象的图像资源
         public void imageLoaded(Drawable imageDrawable);
     }
}

 

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

在主线程调用代码:

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

  调用异步加载方法:

 

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


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

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


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

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



xml文件大致如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
<? xml version = "1.0" encoding = "utf-8" ?>
 
< LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
               android:layout_width = "fill_parent"
               android:orientation = "vertical"
               android:layout_height = "fill_parent" >
   < ImageView android:id = "@+id/image1" android:layout_height = "wrap_content" android:layout_width = "fill_parent" ></ ImageView >
    < ImageView android:id = "@+id/image2" android:layout_height = "wrap_content" android:layout_width = "fill_parent" ></ ImageView >
     < ImageView android:id = "@+id/image3" android:layout_height = "wrap_content" android:layout_width = "fill_parent" ></ ImageView >
     < ImageView android:id = "@+id/image5" android:layout_height = "wrap_content" android:layout_width = "fill_parent" ></ ImageView >
     < ImageView android:id = "@+id/image4" android:layout_height = "wrap_content" android:layout_width = "fill_parent" ></ ImageView >
</ LinearLayout >

转自:http://blog.csdn.net/itachi85/article/details/7589660

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值