研究了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 >
|
相关推荐
在IT行业中,异步编程、线程池和线程管理是优化应用程序性能和响应速度的关键技术,尤其是在处理网络请求如加载图片这样的IO密集型任务时。让我们深入探讨这些概念及其在实际应用中的重要性。 首先,异步编程是一种...
本文将深入探讨如何实现一个高性能的ListView,结合线程池和缓存管理来优化网络图片的加载。 首先,理解ListView的滚动优化至关重要。当ListView滚动时,为了提高性能,只有当前可见的item会进行渲染。这意味着我们...
在Android开发中,图片加载和缓存管理是一个关键的优化环节,尤其是在处理大量图片时,如何避免因内存消耗过大导致的Out Of Memory (OOM)错误至关重要。本资源提供了使用 Imageloader 实现本地图片加载,并结合...
综上所述,异步线程池加载网络图片是Android开发中的关键技术,通过合理的线程管理和缓存策略,我们可以实现高效、流畅的图片加载体验。结合使用第三方库,可以进一步简化开发过程,提升应用性能。
本资料集主要探讨了如何使用线程池和缓存策略来实现高效的Android异步图像加载。以下是相关知识点的详细说明: 1. **异步加载**:在Android中,由于主线程负责UI更新,因此图像加载应当在后台线程执行,以避免阻塞...
为了保证用户体验,我们需要有效地异步加载图像,避免UI线程阻塞,同时考虑到性能优化,使用线程池和缓存策略是必不可少的。本篇文章将深入探讨如何在Android中实现这些技术。 1. **异步加载** - **AsyncTask**:...
在"android中线程池demo"中,我们将探讨如何在Android应用中使用线程池来实现异步加载图片,以提升用户体验。 线程池的概念源于Java的ExecutorService,Android中主要通过`java.util.concurrent`包下的`...
例如,我们可以在后台线程池中加载图片或执行网络请求,然后在主线程中更新UI,遵循Android的"不要阻塞UI线程"的最佳实践。 `Service`是Android组件之一,用于在后台执行长时间运行的任务。虽然`Service`本身并不...
综上所述,"Android异步加载图片示例,内存,SD卡双缓存(优化版)"是一个综合性的图片加载优化实践,它利用多线程、线程池、滚动优化策略以及双缓存机制,有效地解决了Android中图片加载的问题。这个项目对于学习...
在PhotosWallDemo项目中,你应该能看到这样一个功能完善的图片加载器的实现,包括了缓存策略、线程池管理和图片显示逻辑。通过分析和学习这个示例,开发者可以更好地理解和实践高效图片加载技术,提升应用性能。
在Android中,我们通常使用`java.util.concurrent`包下的`ExecutorService`和`ThreadPoolExecutor`来创建线程池。线程池可以避免频繁创建和销毁线程带来的性能开销,提高系统资源的利用率。通过合理配置线程池的参数...
总结起来,这个项目是关于如何在Android应用中使用线程池进行图片的异步加载,以优化性能并提高用户体验。通过合理配置线程池,我们可以有效地控制并发任务的数量,同时避免了主线程被阻塞,保证了应用的流畅运行。...
本文将深入探讨如何实现“Android网络和本地图片加载,支持异步加载并缓存”的技术要点。 首先,我们要理解为什么需要异步加载。在Android系统中,如果在主线程中执行耗时操作,如网络请求或大图片的解码,会导致UI...
本文将深入探讨“Android图片异步加载(双缓存)实例”,介绍如何利用线程池管理和实现内存及SD卡双缓存机制,以提高图片加载效率和用户体验。 1. **异步加载图片** 在Android中,图片加载必须在后台线程执行,以...
本资源为一份关于Android项目中异步加载图像的详细文档,包含了线程池和缓存方法的应用。文档旨在帮助开发者解决在Android应用中高效加载大量图像的问题,提高应用的性能和用户体验。 主要内容包括: 1. 异步加载...
1. **Universal Image Loader (UIL)**:这是一个流行的Android图像加载库,支持内存和磁盘缓存,提供了线程池和图片占位符等功能,方便开发者使用。 2. **Picasso**:由Square公司开发,它简化了图像加载流程,自动...
本Demo "Android网络加载图片GridView缓存处理Demo" 主要关注如何高效地从网络加载图片,并利用内存缓存机制来优化性能。在这个项目中,我们使用了两个关键组件:GridView 和 LruCache。 首先,让我们详细了解`...