实例演示Android异步加载图片
本文给大家演示异步加载图片的分析过程。让大家了解异步加载图片的好处,以及如何更新UI。
首先给出main.xml布局文件:
简单来说就是 LinearLayout 布局,其下放了2个TextView和5个ImageView。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:text="图片区域开始"
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/imageView1"
android:layout_height="wrap_content"
android:src="@drawable/icon"
android:layout_width="wrap_content" />
<ImageView
android:id="@+id/imageView2"
android:layout_height="wrap_content"
android:src="@drawable/icon"
android:layout_width="wrap_content" />
<ImageView
android:id="@+id/imageView3"
android:layout_height="wrap_content"
android:src="@drawable/icon"
android:layout_width="wrap_content" />
<ImageView
android:id="@+id/imageView4"
android:layout_height="wrap_content"
android:src="@drawable/icon"
android:layout_width="wrap_content" />
<ImageView
android:id="@+id/imageView5"
android:layout_height="wrap_content"
android:src="@drawable/icon"
android:layout_width="wrap_content" />
<TextView
android:text="图片区域结束"
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
我们将演示的过程是异步从服务器上下载5张不同图片,依次放入这5个ImageView。上下2个TextView 是为了方便我们看是否阻塞了UI的显示。
当然 AndroidManifest.xml 文件中要配置好网络访问权限。
<uses-permission android:name="android.permission.INTERNET" />
1)Handler+Runnable模式
我们先看一个并不是异步线程加载的例子,而是使用 Handler+Runnable模式。
注意这里不是新开的线程,这里的代码其实是在UI主线程中下载图片的。
我们运行下面代码时,会发现它其实是阻塞了整个界面的显示,需要所有图片都加载完成后,才能显示界面。
package com.szy.textviewimagedemo; import java.io.IOException;
import java.net.URL; import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import android.widget.ImageView; /**
*@author coolszy
*@date 2012-2-13
*@blog http://blog.92coding.com
*
*/
public class MainActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
loadImage("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
loadImage("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.imageView2);
loadImage("http://cache.soso.com/30d/img/web/logo.gif", R.id.imageView3);
loadImage("http://csdnimg.cn/www/images/csdnindex_logo.gif", R.id.imageView4);
loadImage("http://images.cnblogs.com/logo_small.gif", R.id.imageView5);
} private Handler handler = 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.gif");
} catch (IOException e)
{
Log.i("MainActivity", e.getMessage());
}
if (drawable == null)
{
Log.i("MainActivity", "null drawable");
} else
{
Log.i("MainActivity", "not null drawable");
}
// 为了测试缓存而模拟的网络延时
SystemClock.sleep(2000);
((ImageView) MainActivity.this.findViewById(id)).setImageDrawable(drawable);
}
});
}
}
2)Handler+Thread+Message模式
这种模式使用了线程,所以可以看到异步加载的效果。
核心代码:
package com.szy.textviewimagedemo; import java.io.IOException;
import java.net.URL; import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import android.widget.ImageView; /**
*@author coolszy
*@date 2012-2-13
*@blog http://blog.92coding.com
*
*/
public class MainActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.i("MainActivity", "MainThread ID:"+Thread.currentThread().getId());
loadImage("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
loadImage("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.imageView2);
loadImage("http://cache.soso.com/30d/img/web/logo.gif", R.id.imageView3);
loadImage("http://csdnimg.cn/www/images/csdnindex_logo.gif", R.id.imageView4);
loadImage("http://images.cnblogs.com/logo_small.gif", R.id.imageView5);
} final Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
Log.i("MainActivity", "UpdateUIThread ID:"+Thread.currentThread().getId());
((ImageView) MainActivity.this.findViewById(msg.arg1)).setImageDrawable((Drawable) msg.obj);
}
}; // 采用handler+Thread模式实现多线程异步加载
private void loadImage(final String url, final int id)
{
Thread thread = new Thread()
{
@Override
public void run()
{
Drawable drawable = null;
try
{
Log.i("MainActivity", "Thread ID:"+Thread.currentThread().getId());
drawable = Drawable.createFromStream(new URL(url).openStream(), "image.png");
} catch (IOException e)
{
Log.i("MainActivity", e.getMessage());
} // 模拟网络延时
SystemClock.sleep(2000); Message message = handler.obtainMessage();
message.arg1 = id;
message.obj = drawable;
handler.sendMessage(message);
}
};
thread.start();
thread = null;
}
}
这时候我们可以看到实现了异步加载, 界面打开时,五个ImageView都是没有图的,然后在各自线程下载完后才把图自动更新上去。
3)Handler+ExecutorService(线程池)+MessageQueue模式
能开线程的个数毕竟是有限的,我们总不能开很多线程,对于手机更是如此。
这个例子是使用线程池。Android拥有与Java相同的ExecutorService实现,我们就使用它。
线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。
下面的演示例子是创建一个可重用固定线程数的线程池。
核心代码
package com.szy.textviewimagedemo; import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import android.widget.ImageView; /**
*@author coolszy
*@date 2012-2-13
*@blog http://blog.92coding.com
*
*/
public class MainActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.i("MainActivity", "MainThread ID:"+Thread.currentThread().getId());
loadImage("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
loadImage("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.imageView2);
loadImage("http://cache.soso.com/30d/img/web/logo.gif", R.id.imageView3);
loadImage("http://csdnimg.cn/www/images/csdnindex_logo.gif", R.id.imageView4);
loadImage("http://images.cnblogs.com/logo_small.gif", R.id.imageView5);
} private Handler handler = new Handler(); private ExecutorService executorService = Executors.newFixedThreadPool(5); // 引入线程池来管理多线程
private void loadImage(final String url, final int id)
{
executorService.submit(new Runnable()
{
public void run()
{
try
{
Log.i("MainActivity", "Thread ID:"+Thread.currentThread().getId());
final Drawable drawable = Drawable.createFromStream(new URL(url).openStream(), "image.png");
// 模拟网络延时
SystemClock.sleep(2000);
handler.post(new Runnable()
{
public void run()
{
Log.i("MainActivity", "UpdateUIThread ID:"+Thread.currentThread().getId());
((ImageView) MainActivity.this.findViewById(id)).setImageDrawable(drawable);
}
});
} catch (Exception e)
{
throw new RuntimeException(e);
}
}
});
}
}
这里我们象第一步一样使用了
handler.post(new Runnable() { }) 更新前段显示当然是在UI主线程,我们还有 executorService.submit(new Runnable() { }) 来确保下载是在线程池的线程中。
4)Handler+ExecutorService(线程池)+MessageQueue+缓存模式
下面比起前一个做了几个改造:
把整个代码封装在一个类中,同时为了避免出现同时多次下载同一幅图的问题,使用了本地缓存封装的类:
package com.szy.textviewimagedemo; import java.lang.ref.SoftReference;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log; /**
*@author coolszy
*@date 2012-2-13
*@blog http://blog.92coding.com
*/ public class AsyncImageLoader
{
// 为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在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)
{
Log.i("MainActivity", "图片存在缓存中.");
return softReference.get();
}
}
// 缓存中没有图像,则从网络上取出数据,并将取出的数据缓存到内存中
executorService.submit(new Runnable()
{
public void run()
{
try
{
Log.i("MainActivity", "下载图片...");
final Drawable drawable = loadImageFromUrl(imageUrl);
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
{
// 测试时,模拟网络延时,实际时这行代码不能有
SystemClock.sleep(2000);
return Drawable.createFromStream(new URL(imageUrl).openStream(), "image.png"); } catch (Exception e)
{
throw new RuntimeException(e);
}
} // 对外界开放的回调接口
public interface ImageCallback
{
// 注意 此方法是用来设置目标对象的图像资源
public void imageLoaded(Drawable imageDrawable);
}
}
说明:
final参数是指当函数参数为final类型时,你可以读取使用该参数,但是无法改变该参数的值。
这里使用SoftReference 是为了解决内存不足的错误(OutOfMemoryError)的。
前端调用:
package com.szy.textviewimagedemo; import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.ImageView; /**
*@author coolszy
*@date 2012-2-13
*@blog http://blog.92coding.com
*
*/
public class MainActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
loadImage("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1);
loadImage("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.imageView2);
loadImage("http://cache.soso.com/30d/img/web/logo.gif", R.id.imageView3);
loadImage("http://csdnimg.cn/www/images/csdnindex_logo.gif", R.id.imageView4);
loadImage("http://images.cnblogs.com/logo_small.gif", R.id.imageView5);
} private AsyncImageLoader asyncImageLoader = new AsyncImageLoader(); // 引入线程池,并引入内存缓存功能,并对外部调用封装了接口,简化调用过程
private void loadImage(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);
}
}
}

实例演示Android异步加载图片的更多相关文章
- 实例演示Android异步加载图片(转)
本文给大家演示异步加载图片的分析过程.让大家了解异步加载图片的好处,以及如何更新UI.首先给出main.xml布局文件:简单来说就是 LinearLayout 布局,其下放了2个TextView和5个 ...
- 演化理解 Android 异步加载图片(转)
演化理解 Android 异步加载图片(转)http://www.cnblogs.com/CJzhang/archive/2011/10/20/2218474.html
- 演化理解 Android 异步加载图片
原文:http://www.cnblogs.com/ghj1976/archive/2011/05/06/2038738.html#3018499 在学习"Android异步加载图像小结&q ...
- [Android]异步加载图片,内存缓存,文件缓存,imageview显示图片时增加淡入淡出动画
以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/3574131.html 这个可以实现ImageView异步加载 ...
- android异步加载图片并缓存到本地实现方法
图片过多造成内存溢出,这个是最不容易解决的,要想一些好的缓存策略,比如大图片使用LRU缓存策略或懒加载缓存策略.今天首先介绍一下本地缓存图片 在android项目中访问网络图片是非常普遍性的事 ...
- Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
Android 高手进阶(21) 版权声明:本文为博主原创文章,未经博主允许不得转载. 转载请注明出处http://blog.csdn.net/xiaanming/article/details ...
- wemall app商城源码中基于JAVA的Android异步加载图片管理器代码
wemall doraemon是Android客户端程序,服务端采用wemall微信商城,不对原商城做任何修改,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可随意定制修改.本文分享其中 ...
- android异步加载图片
import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; impo ...
- Android中ListView异步加载图片错位、重复、闪烁问题分析及解决方案
我们在使用ListView异步加载图片的时候,在快速滑动或者网络不好的情况下,会出现图片错位.重复.闪烁等问题,其实这些问题总结起来就是一个问题,我们需要对这些问题进行ListView的优化. 比如L ...
随机推荐
- CSS3之绽放的花朵(网页效果--每日一更)
今天,带来的是纯CSS3打造的效果--绽放的花朵. 先来看效果吧:亲,请点击这里 这是纯CSS3样式打造的效果,关键是采用了animation属性和transform属性.详细请看下面代码. HTML ...
- linux 2.6 驱动笔记(三)
驱动的并发与应用的并发实现一样,以信号量为例,修改基本字符驱动代码如下: 1. 增加sem定义 struct globalmem_dev{ struct cdev cdev; /*linux 2.6 ...
- 设计模式之美:Null Object(空对象)
索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):Null Object 的示例实现. 意图 通过对缺失对象的封装,以提供默认无任何行为的对象替代品. Encapsulate t ...
- openwrt-智能路由器hack技术(2)---"网路信息监控和窃取"
openwrt-智能路由器hack技术(2)---"网路信息监控和窃取" 1 导读 PS:之前写的一个文章,现在发现结构内容排版不是太好,导致阅读体验太差,影响传播和SEO,所 ...
- 一个App完成入门篇(四)- 完成反馈页面
上一节中我们学会了如何通过点击不同按钮切换页面,这节专注于完成反馈页面的功能以及细节动画. 导入项目 添加新组件 同步新组件 完成页面布局 输入时加动画效果 弹出日期选择 直接引用UI页面 将要学习的 ...
- C#实现的等额本息法、按月付息到期还本法、一次性还本付息法
你若懂行,那便有用,如下: void Main(){ var x = DengEBenXi.Compute(11111, 12, 3); x.Dump(); var y = AnYu ...
- HTML表单入门基础
网页镶嵌: <iframe src="http://www.cnblogs.com/tfl-511/" width="200" height=" ...
- PDO预处理
方法:bool PDOStatement::execute ([ array $input_parameters ] ) 1.PDOStatement::execute不使用参数 01)单个绑定值(P ...
- H5常用代码:适配方案3
在H5项目中有一种常见的宣传页,就是那种整屏整屏的,带着炫丽进场动画的移动宣传页,不仅是一种欣赏也起到了很大宣传作用. 对于这种整屏的适配,前面通过视口的兼容处理也是可以做到的,但是在窄屏下会在上下有 ...
- ASP.net的指令
3.2 ASP.NET指令 在基于面向对象思想的.NET平台,可以称之为“万物皆对象”了.在这里,一个页面,一个用户控件,一个母版页等,全都是对象,全都有各自的属性. 在类文件里,我们表示类的属性可以 ...