实例演示Android异步加载图片

本文给大家演示异步加载图片的分析过程。让大家了解异步加载图片的好处,以及如何更新UI。
首先给出main.xml布局文件:
简单来说就是 LinearLayout 布局,其下放了2个TextView和5个ImageView。

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout
 3     xmlns:android="http://schemas.android.com/apk/res/android"
 4     android:orientation="vertical"
 5     android:layout_width="fill_parent"
 6     android:layout_height="fill_parent">
 7     <TextView
 8         android:text="图片区域开始"
 9         android:id="@+id/textView2"
10         android:layout_width="wrap_content"
11         android:layout_height="wrap_content" />
12     <ImageView
13         android:id="@+id/imageView1"
14         android:layout_height="wrap_content"
15         android:src="@drawable/icon"
16         android:layout_width="wrap_content" />
17     <ImageView
18         android:id="@+id/imageView2"
19         android:layout_height="wrap_content"
20         android:src="@drawable/icon"
21         android:layout_width="wrap_content" />
22     <ImageView
23         android:id="@+id/imageView3"
24         android:layout_height="wrap_content"
25         android:src="@drawable/icon"
26         android:layout_width="wrap_content" />
27     <ImageView
28         android:id="@+id/imageView4"
29         android:layout_height="wrap_content"
30         android:src="@drawable/icon"
31         android:layout_width="wrap_content" />
32     <ImageView
33         android:id="@+id/imageView5"
34         android:layout_height="wrap_content"
35         android:src="@drawable/icon"
36         android:layout_width="wrap_content" />
37     <TextView
38         android:text="图片区域结束"
39         android:id="@+id/textView1"
40         android:layout_width="wrap_content"
41         android:layout_height="wrap_content" />
42 </LinearLayout>

我们将演示的过程是异步从服务器上下载5张不同图片,依次放入这5个ImageView。上下2个TextView 是为了方便我们看是否阻塞了UI的显示。
当然 AndroidManifest.xml 文件中要配置好网络访问权限。

1 <uses-permission android:name="android.permission.INTERNET" />

1)Handler+Runnable模式
我们先看一个并不是异步线程加载的例子,而是使用 Handler+Runnable模式。
注意这里不是新开的线程,这里的代码其实是在UI主线程中下载图片的。
我们运行下面代码时,会发现它其实是阻塞了整个界面的显示,需要所有图片都加载完成后,才能显示界面。

 1 package com.szy.textviewimagedemo;
 2
 3 import java.io.IOException;
 4 import java.net.URL;
 5
 6 import android.app.Activity;
 7 import android.graphics.drawable.Drawable;
 8 import android.os.Bundle;
 9 import android.os.Handler;
10 import android.os.SystemClock;
11 import android.util.Log;
12 import android.widget.ImageView;
13
14 /**
15  *@author coolszy
16  *@date 2012-2-13
17  *@blog http://blog.92coding.com
18  *
19  */
20 public class MainActivity extends Activity
21 {
22     @Override
23     public void onCreate(Bundle savedInstanceState)
24     {
25         super.onCreate(savedInstanceState);
26         setContentView(R.layout.main);
27         loadImage("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
28         loadImage("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.imageView2);
29         loadImage("http://cache.soso.com/30d/img/web/logo.gif", R.id.imageView3);
30         loadImage("http://csdnimg.cn/www/images/csdnindex_logo.gif", R.id.imageView4);
31         loadImage("http://images.cnblogs.com/logo_small.gif", R.id.imageView5);
32     }
33
34     private Handler handler = new Handler();
35
36     private void loadImage(final String url, final int id)
37     {
38         handler.post(new Runnable()
39         {
40             public void run()
41             {
42                 Drawable drawable = null;
43                 try
44                 {
45                     drawable = Drawable.createFromStream(new URL(url).openStream(), "image.gif");
46                 } catch (IOException e)
47                 {
48                     Log.i("MainActivity", e.getMessage());
49                 }
50                 if (drawable == null)
51                 {
52                     Log.i("MainActivity", "null drawable");
53                 } else
54                 {
55                     Log.i("MainActivity", "not null drawable");
56                 }
57                 // 为了测试缓存而模拟的网络延时
58                 SystemClock.sleep(2000);
59                 ((ImageView) MainActivity.this.findViewById(id)).setImageDrawable(drawable);
60             }
61         });
62     }
63 }

2)Handler+Thread+Message模式
这种模式使用了线程,所以可以看到异步加载的效果。
核心代码:

 1 package com.szy.textviewimagedemo;
 2
 3 import java.io.IOException;
 4 import java.net.URL;
 5
 6 import android.app.Activity;
 7 import android.graphics.drawable.Drawable;
 8 import android.os.Bundle;
 9 import android.os.Handler;
10 import android.os.Message;
11 import android.os.SystemClock;
12 import android.util.Log;
13 import android.widget.ImageView;
14
15 /**
16  *@author coolszy
17  *@date 2012-2-13
18  *@blog http://blog.92coding.com
19  *
20  */
21 public class MainActivity extends Activity
22 {
23     @Override
24     public void onCreate(Bundle savedInstanceState)
25     {
26         super.onCreate(savedInstanceState);
27         setContentView(R.layout.main);
28         Log.i("MainActivity", "MainThread ID:"+Thread.currentThread().getId());
29         loadImage("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
30         loadImage("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.imageView2);
31         loadImage("http://cache.soso.com/30d/img/web/logo.gif", R.id.imageView3);
32         loadImage("http://csdnimg.cn/www/images/csdnindex_logo.gif", R.id.imageView4);
33         loadImage("http://images.cnblogs.com/logo_small.gif", R.id.imageView5);
34     }
35
36     final Handler handler = new Handler()
37     {
38         @Override
39         public void handleMessage(Message msg)
40         {
41             Log.i("MainActivity", "UpdateUIThread ID:"+Thread.currentThread().getId());
42             ((ImageView) MainActivity.this.findViewById(msg.arg1)).setImageDrawable((Drawable) msg.obj);
43         }
44     };
45
46     // 采用handler+Thread模式实现多线程异步加载
47     private void loadImage(final String url, final int id)
48     {
49         Thread thread = new Thread()
50         {
51             @Override
52             public void run()
53             {
54                 Drawable drawable = null;
55                 try
56                 {
57                     Log.i("MainActivity", "Thread ID:"+Thread.currentThread().getId());
58                     drawable = Drawable.createFromStream(new URL(url).openStream(), "image.png");
59                 } catch (IOException e)
60                 {
61                     Log.i("MainActivity", e.getMessage());
62                 }
63
64                 // 模拟网络延时
65                 SystemClock.sleep(2000);
66
67                 Message message = handler.obtainMessage();
68                 message.arg1 = id;
69                 message.obj = drawable;
70                 handler.sendMessage(message);
71             }
72         };
73         thread.start();
74         thread = null;
75     }
76 }

这时候我们可以看到实现了异步加载, 界面打开时,五个ImageView都是没有图的,然后在各自线程下载完后才把图自动更新上去。
3)Handler+ExecutorService(线程池)+MessageQueue模式
能开线程的个数毕竟是有限的,我们总不能开很多线程,对于手机更是如此。
这个例子是使用线程池。Android拥有与Java相同的ExecutorService实现,我们就使用它。
线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。
下面的演示例子是创建一个可重用固定线程数的线程池。
核心代码

 1 package com.szy.textviewimagedemo;
 2
 3 import java.net.URL;
 4 import java.util.concurrent.ExecutorService;
 5 import java.util.concurrent.Executors;
 6
 7 import android.app.Activity;
 8 import android.graphics.drawable.Drawable;
 9 import android.os.Bundle;
10 import android.os.Handler;
11 import android.os.SystemClock;
12 import android.util.Log;
13 import android.widget.ImageView;
14
15 /**
16  *@author coolszy
17  *@date 2012-2-13
18  *@blog http://blog.92coding.com
19  *
20  */
21 public class MainActivity extends Activity
22 {
23     @Override
24     public void onCreate(Bundle savedInstanceState)
25     {
26         super.onCreate(savedInstanceState);
27         setContentView(R.layout.main);
28         Log.i("MainActivity", "MainThread ID:"+Thread.currentThread().getId());
29         loadImage("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
30         loadImage("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.imageView2);
31         loadImage("http://cache.soso.com/30d/img/web/logo.gif", R.id.imageView3);
32         loadImage("http://csdnimg.cn/www/images/csdnindex_logo.gif", R.id.imageView4);
33         loadImage("http://images.cnblogs.com/logo_small.gif", R.id.imageView5);
34     }
35
36     private Handler handler = new Handler();
37
38     private ExecutorService executorService = Executors.newFixedThreadPool(5);
39
40     // 引入线程池来管理多线程
41     private void loadImage(final String url, final int id)
42     {
43         executorService.submit(new Runnable()
44         {
45             public void run()
46             {
47                 try
48                 {
49                     Log.i("MainActivity", "Thread ID:"+Thread.currentThread().getId());
50                     final Drawable drawable = Drawable.createFromStream(new URL(url).openStream(), "image.png");
51                     // 模拟网络延时
52                     SystemClock.sleep(2000);
53                     handler.post(new Runnable()
54                     {
55                         public void run()
56                         {
57                             Log.i("MainActivity", "UpdateUIThread ID:"+Thread.currentThread().getId());
58                             ((ImageView) MainActivity.this.findViewById(id)).setImageDrawable(drawable);
59                         }
60                     });
61                 } catch (Exception e)
62                 {
63                     throw new RuntimeException(e);
64                 }
65             }
66         });
67     }
68 }

这里我们象第一步一样使用了
handler.post(new Runnable() { }) 更新前段显示当然是在UI主线程,我们还有 executorService.submit(new Runnable() { }) 来确保下载是在线程池的线程中。
4)Handler+ExecutorService(线程池)+MessageQueue+缓存模式
下面比起前一个做了几个改造:
把整个代码封装在一个类中,同时为了避免出现同时多次下载同一幅图的问题,使用了本地缓存封装的类:

 1 package com.szy.textviewimagedemo;
 2
 3 import java.lang.ref.SoftReference;
 4 import java.net.URL;
 5 import java.util.HashMap;
 6 import java.util.Map;
 7 import java.util.concurrent.ExecutorService;
 8 import java.util.concurrent.Executors;
 9
10 import android.graphics.drawable.Drawable;
11 import android.os.Handler;
12 import android.os.SystemClock;
13 import android.util.Log;
14
15 /**
16  *@author coolszy
17  *@date 2012-2-13
18  *@blog http://blog.92coding.com
19  */
20
21 public class AsyncImageLoader
22 {
23     // 为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动)
24     public Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();
25
26     private ExecutorService executorService = Executors.newFixedThreadPool(5); // 固定五个线程来执行任务
27     private final Handler handler = new Handler();
28
29     /**
30      *
31      * @param imageUrl
32      *            图像url地址
33      * @param callback
34      *            回调接口
35      * @return 返回内存中缓存的图像,第一次加载返回null
36      */
37     public Drawable loadDrawable(final String imageUrl, final ImageCallback callback)
38     {
39         // 如果缓存过就从缓存中取出数据
40         if (imageCache.containsKey(imageUrl))
41         {
42             SoftReference<Drawable> softReference = imageCache.get(imageUrl);
43             if (softReference.get() != null)
44             {
45                 Log.i("MainActivity", "图片存在缓存中.");
46                 return softReference.get();
47             }
48         }
49         // 缓存中没有图像,则从网络上取出数据,并将取出的数据缓存到内存中
50         executorService.submit(new Runnable()
51         {
52             public void run()
53             {
54                 try
55                 {
56                     Log.i("MainActivity", "下载图片...");
57                     final Drawable drawable = loadImageFromUrl(imageUrl);
58                     imageCache.put(imageUrl, new SoftReference<Drawable>(drawable));
59                     handler.post(new Runnable()
60                     {
61                         public void run()
62                         {
63                             callback.imageLoaded(drawable);
64                         }
65                     });
66                 } catch (Exception e)
67                 {
68                     throw new RuntimeException(e);
69                 }
70             }
71         });
72         return null;
73     }
74
75     // 从网络上取数据方法
76     protected Drawable loadImageFromUrl(String imageUrl)
77     {
78         try
79         {
80             // 测试时,模拟网络延时,实际时这行代码不能有
81             SystemClock.sleep(2000);
82             return Drawable.createFromStream(new URL(imageUrl).openStream(), "image.png");
83
84         } catch (Exception e)
85         {
86             throw new RuntimeException(e);
87         }
88     }
89
90     // 对外界开放的回调接口
91     public interface ImageCallback
92     {
93         // 注意 此方法是用来设置目标对象的图像资源
94         public void imageLoaded(Drawable imageDrawable);
95     }
96 }

说明:
final参数是指当函数参数为final类型时,你可以读取使用该参数,但是无法改变该参数的值。
这里使用SoftReference 是为了解决内存不足的错误(OutOfMemoryError)的。
前端调用:

 1 package com.szy.textviewimagedemo;
 2
 3 import android.app.Activity;
 4 import android.graphics.drawable.Drawable;
 5 import android.os.Bundle;
 6 import android.widget.ImageView;
 7
 8 /**
 9  *@author coolszy
10  *@date 2012-2-13
11  *@blog http://blog.92coding.com
12  *
13  */
14 public class MainActivity extends Activity
15 {
16     @Override
17     public void onCreate(Bundle savedInstanceState)
18     {
19         super.onCreate(savedInstanceState);
20         setContentView(R.layout.main);
21         loadImage("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
22         loadImage("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.imageView2);
23         loadImage("http://cache.soso.com/30d/img/web/logo.gif", R.id.imageView3);
24         loadImage("http://csdnimg.cn/www/images/csdnindex_logo.gif", R.id.imageView4);
25         loadImage("http://images.cnblogs.com/logo_small.gif", R.id.imageView5);
26     }
27
28     private AsyncImageLoader asyncImageLoader = new AsyncImageLoader();
29
30     // 引入线程池,并引入内存缓存功能,并对外部调用封装了接口,简化调用过程
31     private void loadImage(final String url, final int id)
32     {
33         // 如果缓存过就会从缓存中取出图像,ImageCallback接口中方法也不会被执行
34         Drawable cacheImage = asyncImageLoader.loadDrawable(url, new AsyncImageLoader.ImageCallback()
35         {
36             // 请参见实现:如果第一次加载url时下面方法会执行
37             public void imageLoaded(Drawable imageDrawable)
38             {
39                 ((ImageView) findViewById(id)).setImageDrawable(imageDrawable);
40             }
41         });
42         if (cacheImage != null)
43         {
44             ((ImageView) findViewById(id)).setImageDrawable(cacheImage);
45         }
46     }
47 }

时间: 2024-10-27 19:09:19

实例演示Android异步加载图片的相关文章

实例演示Android异步加载图片(转)

本文给大家演示异步加载图片的分析过程.让大家了解异步加载图片的好处,以及如何更新UI.首先给出main.xml布局文件:简单来说就是 LinearLayout 布局,其下放了2个TextView和5个ImageView. 1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout 3 xmlns:android="http://schemas.android.com/apk/res/a

我的Android进阶之旅------&amp;gt;android异步加载图片显示,并且对图片进行缓存实例

先来看看效果示意图: step1:新建项目DataAsyncLoad,如下图所示 step2:设置应用的UI界面 a.应用的主界面    main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="

Android 异步加载图片的实例代码_Android

异步加载图片的主要流程是进行判断缓存中是否存在图片,如果存在则直接返回,如果不存在则进行下载并进行缓存. 以下是建立一个异步下载类: 复制代码 代码如下: /** * User: Tom * Date: 13-5-13 * Time: 下午8:07 */public class AsnycImageLoader {     //定义一个HashMap进行存放缓存的Image key为String Value为一个弱引用的一个资源文件    // 图片 为了方便JAVA的回收    private

Android 异步加载图片的实例代码

异步加载图片的主要流程是进行判断缓存中是否存在图片,如果存在则直接返回,如果不存在则进行下载并进行缓存. 以下是建立一个异步下载类:复制代码 代码如下:/** * User: Tom * Date: 13-5-13 * Time: 下午8:07 */public class AsnycImageLoader { //定义一个HashMap进行存放缓存的Image key为String Value为一个弱引用的一个资源文件    // 图片 为了方便JAVA的回收    private Map<S

Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅

异步加载图片的例子,网上也比较多,大部分用了HashMap<String, SoftReference<Drawable>> imageCache ,但是现在已经不再推荐使用这种方式了,因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用和弱引用变得不再可靠.另外,Android 3.0 (API Level 11)中,图片的数据会存储在本地的内存当中,因而无法用一种可预见的方式将其释放,这就有潜在的风险造成应

[Android]异步加载图片,内存缓存,文件缓存,imageview显示图片时增加淡入淡出动画

以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/3574131.html  这个可以实现ImageView异步加载图片,内存缓存,文件缓存,imageview显示图片时增加淡入淡出动画. github地址:https://github.com/wangjiegulu/ImageLoaderSample 解决了: 1. listview加载oom问题 2. listview加载时卡顿的现象 3. listview

android异步加载图片并缓存到本地实现方法

在android项目中访问网络图片是非常普遍性的事情,如果我们每次请求都要访问网络来获取图片,会非常耗费流量,而且图片占用内存空间也比较大,图片过多且不释放的话很容易造成内存溢出.针对上面遇到的两个问题,首先耗费流量我们可以将图片第一次加载上面缓存到本地,以后如果本地有就直接从本地加载.图片过多造成内存溢出,这个是最不容易解决的,要想一些好的缓存策略,比如大图片使用LRU缓存策略或懒加载缓存策略.今天首先介绍一下本地缓存图片. 首先看一下异步加载缓存本地代码: 复制代码 代码如下: public

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

Android 异步加载图片分析总结

研究了android从网络上异步加载图像,现总结如下: (1)由于android UI更新支持单一线程原则,所以从网络上取数据并更新到界面上,为了不阻塞主线程首先可能会想到以下方法. 在主线程中new 一个Handler对象,加载图像方法如下所示 复制代码 代码如下: private void loadImage(final String url, final int id) { handler.post(new Runnable() { public void run() { Drawable