研究了android从网络上异步加载图像:
(1)由于android UI更新支持单一线程原则,所以从网络上取数据并更新到界面上,为了不阻塞主线程首先可能会想到以下方法。
在主线程中new 一个Handler对象,加载图像方法如下所示
01 |
private void loadImage( final String url, final int id) {
|
02 |
handler.post( new Runnable() {
|
03 |
public void run() {
|
04 |
Drawable drawable = null ;
|
05 |
try {
|
06 |
drawable = Drawable.createFromStream( new URL(url).openStream(), "image.png" );
|
07 |
} catch (IOException e) {
|
08 |
}
|
09 |
((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 |
};
|
对应加载图像代码如下:对应加载图像代码如下:对应加载图像代码如下:
01 |
// 引入线程池来管理多线程 |
02 |
private void loadImage3( final String url, final int id) {
|
03 |
executorService.submit( new Runnable() {
|
04 |
public void run() {
|
05 |
try {
|
06 |
final Drawable drawable = Drawable.createFromStream( new URL(url).openStream(), "image.png" );
|
07 |
handler.post( new Runnable() {
|
08 |
09 |
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。先看一下我们封装的类
01 |
public class AsyncImageLoader3 {
|
02 |
//为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动)
|
03 |
public Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();
|
04 |
private ExecutorService executorService = Executors.newFixedThreadPool( 5 ); //固定五个线程来执行任务
|
05 |
private final Handler handler= new Handler();
|
06 |
|
07 |
/**
|
08 |
*
|
09 |
* @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();
调用异步加载方法:
01 |
//引入线程池,并引入内存缓存功能,并对外部调用封装了接口,简化调用过程 |
02 |
private void loadImage4( final String url, final int id) {
|
03 |
//如果缓存过就会从缓存中取出图像,ImageCallback接口中方法也不会被执行
|
04 |
Drawable cacheImage = asyncImageLoader.loadDrawable(url, new AsyncImageLoader.ImageCallback() {
|
05 |
//请参见实现:如果第一次加载url时下面方法会执行
|
06 |
public void imageLoaded(Drawable imageDrawable) {
|
07 |
((ImageView) findViewById(id)).setImageDrawable(imageDrawable);
|
08 |
}
|
09 |
});
|
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模式而已。代码如下:
01 |
public class AsyncImageLoader {
|
02 |
//为了加快速度,加入了缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在ListView时来回滚动)
|
03 |
private Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();
|
04 |
|
05 |
/**
|
06 |
*
|
07 |
* @param imageUrl 图像url地址
|
08 |
* @param callback 回调接口
|
09 |
* @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 |
} |
至此,异步加载就介绍完了,下面给出的代码为测试用的完整代码:
001 |
package com.bshark.supertelphone.activity;
|
002 |
|
003 |
import android.app.Activity;
|
004 |
import android.graphics.drawable.Drawable;
|
005 |
import android.os.Bundle;
|
006 |
import android.os.Handler;
|
007 |
import android.os.Message;
|
008 |
import android.widget.ImageView;
|
009 |
import com.bshark.supertelphone.R;
|
010 |
import com.bshark.supertelphone.ui.adapter.util.AsyncImageLoader;
|
011 |
import com.bshark.supertelphone.ui.adapter.util.AsyncImageLoader3;
|
012 |
|
013 |
import java.io.IOException;
|
014 |
import java.net.URL;
|
015 |
import java.util.concurrent.ExecutorService;
|
016 |
import java.util.concurrent.Executors;
|
017 |
|
018 |
public class LazyLoadImageActivity extends Activity {
|
019 |
final Handler handler= new Handler();
|
020 |
final Handler handler2= new Handler(){
|
021 |
@Override
|
022 |
public void handleMessage(Message msg) {
|
023 |
((ImageView) LazyLoadImageActivity. this .findViewById(msg.arg1)).setImageDrawable((Drawable)msg.obj);
|
024 |
}
|
025 |
};
|
026 |
private ExecutorService executorService = Executors.newFixedThreadPool( 5 ); //固定五个线程来执行任务
|
027 |
private AsyncImageLoader asyncImageLoader = new AsyncImageLoader();
|
028 |
private AsyncImageLoader3 asyncImageLoader3 = new AsyncImageLoader3();
|
029 |
|
030 |
|
031 |
@Override |
032 |
public void onCreate(Bundle savedInstanceState) {
|
033 |
super .onCreate(savedInstanceState);
|
034 |
setContentView(R.layout.main);
|
035 |
|
036 |
// loadImage("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.image1); |
037 |
// loadImage("http://www.baidu.com/img/baidu_logo.gif", R.id.image2); |
038 |
// loadImage("http://cache.soso.com/30d/img/web/logo.gif", R.id.image3); |
039 |
// loadImage("http://www.baidu.com/img/baidu_logo.gif", R.id.image4); |
040 |
// loadImage("http://cache.soso.com/30d/img/web/logo.gif", R.id.image5); |
041 |
|
042 |
loadImage2( "http://www.chinatelecom.com.cn/images/logo_new.gif" , R.id.image1);
|
043 |
loadImage2( "http://www.baidu.com/img/baidu_logo.gif" , R.id.image2);
|
044 |
loadImage2( "http://cache.soso.com/30d/img/web/logo.gif" , R.id.image3);
|
045 |
loadImage2( "http://www.baidu.com/img/baidu_logo.gif" , R.id.image4);
|
046 |
loadImage2( "http://cache.soso.com/30d/img/web/logo.gif" , R.id.image5);
|
047 |
// loadImage3("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.image1); |
048 |
// loadImage3("http://www.baidu.com/img/baidu_logo.gif", R.id.image2); |
049 |
// loadImage3("http://cache.soso.com/30d/img/web/logo.gif", R.id.image3); |
050 |
// loadImage3("http://www.baidu.com/img/baidu_logo.gif", R.id.image4); |
051 |
// loadImage3("http://cache.soso.com/30d/img/web/logo.gif", R.id.image5); |
052 |
|
053 |
// loadImage4("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.image1); |
054 |
// loadImage4("http://www.baidu.com/img/baidu_logo.gif", R.id.image2); |
055 |
// loadImage4("http://cache.soso.com/30d/img/web/logo.gif", R.id.image3); |
056 |
// loadImage4("http://www.baidu.com/img/baidu_logo.gif", R.id.image4); |
057 |
// loadImage4("http://cache.soso.com/30d/img/web/logo.gif", R.id.image5); |
058 |
|
059 |
// loadImage5("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.image1); |
060 |
// //为了测试缓存而模拟的网络延时 |
061 |
// SystemClock.sleep(2000); |
062 |
// loadImage5("http://www.baidu.com/img/baidu_logo.gif", R.id.image2); |
063 |
// SystemClock.sleep(2000); |
064 |
// loadImage5("http://cache.soso.com/30d/img/web/logo.gif", R.id.image3); |
065 |
// SystemClock.sleep(2000); |
066 |
// loadImage5("http://www.baidu.com/img/baidu_logo.gif", R.id.image4); |
067 |
// SystemClock.sleep(2000); |
068 |
// loadImage5("http://cache.soso.com/30d/img/web/logo.gif", R.id.image5); |
069 |
// SystemClock.sleep(2000); |
070 |
// loadImage5("http://www.baidu.com/img/baidu_logo.gif", R.id.image4); |
071 |
} |
072 |
|
073 |
@Override |
074 |
protected void onDestroy() {
|
075 |
executorService.shutdown();
|
076 |
super .onDestroy();
|
077 |
} |
078 |
//线程加载图像基本原理
|
079 |
private void loadImage( final String url, final int id) {
|
080 |
handler.post( new Runnable() {
|
081 |
public void run() {
|
082 |
Drawable drawable = null ;
|
083 |
try {
|
084 |
drawable = Drawable.createFromStream( new URL(url).openStream(), "image.png" );
|
085 |
} catch (IOException e) {
|
086 |
}
|
087 |
((ImageView) LazyLoadImageActivity. this .findViewById(id)).setImageDrawable(drawable);
|
088 |
}
|
089 |
});
|
090 |
}
|
091 |
//采用handler+Thread模式实现多线程异步加载
|
092 |
private void loadImage2( final String url, final int id) {
|
093 |
Thread thread = new Thread(){
|
094 |
@Override
|
095 |
public void run() {
|
096 |
Drawable drawable = null ;
|
097 |
try {
|
098 |
drawable = Drawable.createFromStream( new URL(url).openStream(), "image.png" );
|
099 |
} 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文件大致如下:
01 |
<? xml version = "1.0" encoding = "utf-8" ?>
|
02 |
|
03 |
< LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
|
04 |
android:layout_width = "fill_parent"
|
05 |
android:orientation = "vertical"
|
06 |
android:layout_height = "fill_parent" >
|
07 |
< ImageView android:id = "@+id/image1" android:layout_height = "wrap_content" android:layout_width = "fill_parent" ></ ImageView >
|
08 |
< ImageView android:id = "@+id/image2" android:layout_height = "wrap_content" android:layout_width = "fill_parent" ></ ImageView >
|
09 |
< 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 >
|
相关推荐
android开发必备知识,android listview 加载网络图片 采用 可管理线程池+文件缓存+网络优化 欢迎下载参考
本资源为一份关于Android项目中异步加载图像的详细文档,包含了线程池和缓存方法的应用。文档旨在帮助开发者解决在Android应用中高效加载大量图像的问题,提高应用的性能和用户体验。 主要内容包括: 1. 异步加载...
Android异步加载图像小结 (含线程池,缓存方法).rar
Android异步加载图像小结 (含线程池,缓存方法).zip
imageloader加载本地图片,利用线程池,缓存,LIFO,防止大量图片加载导致OOM(代码中加有注释理解).rar,太多无法一一验证是否可用,程序如果跑不起来需要自调,部分代码功能进行参考学习。
Android异步加载图像小结 (含线程池,缓存方法)
在listview和gridview等容器加载图片的时候快速滑动无错误现象 支持线程池并发数量配置 支持内存缓存大小配置 支持磁盘大小缓存配置,和缓存位置配置 支持磁盘缓存使用压缩非压缩同时配置,压缩针对某个...
较上一次发布的加载方式进行了优化,测试后发布的,采用线程池,多线程加载图片,滚动时不加载,停止时再加载,重写了ImageLoader类,大家可以下载使用。
Android异步加载图像小结 (含线程池,缓存方法
图片压缩,图片缓存,线程池,listview
Android异步加载图像小结 (含线程池,缓存方法)完整版
异步加载网络图片类,采用线程池管理,内存,sd卡双缓存机制,附带示例。