• 实例演示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 文件中要配置好网络访问权限。 

    <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 }

    转至若水工作室 http://www.92coding.com/blog/index.php/archives/295.html

  • 相关阅读:
    CentOS7 下部署 Iptables 环境纪录(关闭默认的firewalle)
    Linux系统下root密码遗忘等系统故障的修复方法
    LDAP学习笔记总结
    kvm虚拟化关闭虚拟网卡virbr0的方法
    Gradle使用指南
    Android 注解工具 ButterKnife
    Sublime Text + CTags + Cscope (部分替代Source Insight)
    win + linux + android 多任务分屏
    Ubuntu16.04 安装openjdk-7-jdk
    eclipse + Android Studio 集成 Genymotion 模拟器
  • 原文地址:https://www.cnblogs.com/zhangs1986/p/3236521.html
Copyright © 2020-2023  润新知