【转】[Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
android线程池的理解,晚上在家无事 预习了一下android异步加载的例子,也学习到了一个很重要的东东 那就是线程池+缓存 下面看他们的理解。
[size=1.8em]Handler+Runnable模式
我们先看一个并不是异步线程加载的例子,使用 Handler+Runnable模式。
这里为何不是新开线程的原因请参看这篇文章:Android Runnable 运行在那个线程 这里的代码其实是在UI 主线程中下载图片的,而不是新开线程。
我们运行下面代码时,会发现他其实是阻塞了整个界面的显示,需要所有图片都加载完成后,才能显示界面。
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
package ghj1976.AndroidTest;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;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(<img id="\"aimg_qZH5H\"" onclick="\"zoom(this," this.src,="" 0,="" 0)\"="" class="\"zoom\"" file="\"http://www.chinatelecom.com.cn/images/logo_new.gif\"" onmouseover="\"img_onmouseoverfunc(this)\"" lazyloadthumb="\"1\"" border="\"0\"" alt="\"\"">", 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.d("test", e.getMessage()); } if (drawable == null) { Log.d("test", "null drawable"); } else { Log.d("test", "not null drawable"); } // 为了测试缓存而模拟的网络延时 SystemClock.sleep(2000); ((ImageView) MainActivity.this.findViewById(id)) .setImageDrawable(drawable); } }); }} |
Handler+Thread+Message模式
这种模式使用了线程,所以可以看到异步加载的效果。
核心代码:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
package ghj1976.AndroidTest;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;public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); loadImage2("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1); loadImage2("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.imageView2); loadImage2("http://cache.soso.com/30d/img/web/logo.gif", R.id.imageView3); loadImage2("http://csdnimg.cn/www/images/csdnindex_logo.gif", R.id.imageView4); loadImage2("http://images.cnblogs.com/logo_small.gif", R.id.imageView5); } final Handler handler2 = new Handler() { @Override public void handleMessage(Message msg) { ((ImageView) MainActivity.this.findViewById(msg.arg1)) .setImageDrawable((Drawable) msg.obj); } }; // 采用handler+Thread模式实现多线程异步加载 private void loadImage2(final String url, final int id) { Thread thread = new Thread() { @Override public void run() { Drawable drawable = null; try { drawable = Drawable.createFromStream( new URL(url).openStream(), "image.png"); } catch (IOException e) { Log.d("test", e.getMessage()); } // 模拟网络延时 SystemClock.sleep(2000); Message message = handler2.obtainMessage(); message.arg1 = id; message.obj = drawable; handler2.sendMessage(message); } }; thread.start(); thread = null; }} |
这时候我们可以看到实现了异步加载, 界面打开时,五个ImageView都是没有图的,然后在各自线程下载完后才把图自动更新上去。
Handler+ExecutorService(线程池)+MessageQueue模式
能开线程的个数毕竟是有限的,我们总不能开很多线程,对于手机更是如此。
这个例子是使用线程池。Android拥有与Java相同的ExecutorService实现,我们就来用它。
线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。
线程池的信息可以参看这篇文章:Java&Android的线程池-ExecutorService 下面的演示例子是创建一个可重用固定线程数的线程池。
核心代码
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
package ghj1976.AndroidTest;import java.io.IOException;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.Message;import android.os.SystemClock;import android.util.Log;import android.widget.ImageView;public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); loadImage3("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1); loadImage3("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.imageView2); loadImage3("http://cache.soso.com/30d/img/web/logo.gif", R.id.imageView3); loadImage3("http://csdnimg.cn/www/images/csdnindex_logo.gif", R.id.imageView4); loadImage3("http://images.cnblogs.com/logo_small.gif", R.id.imageView5); } private Handler handler = new Handler(); private ExecutorService executorService = Executors.newFixedThreadPool(5); // 引入线程池来管理多线程 private void loadImage3(final String url, final int id) { executorService.submit(new Runnable() { public void run() { try { final Drawable drawable = Drawable.createFromStream( new URL(url).openStream(), "image.png"); // 模拟网络延时 SystemClock.sleep(2000); handler.post(new Runnable() { public void run() { ((ImageView) MainActivity.this.findViewById(id)) .setImageDrawable(drawable); } }); } catch (Exception e) { throw new RuntimeException(e); } } }); }} |
这里我们象第一步一样使用了 handler.post(new Runnable() { 更新前段显示当然是在UI主线程,我们还有 executorService.submit(new Runnable() { 来确保下载是在线程池的线程中。
Handler+ExecutorService(线程池)+MessageQueue+缓存模式
下面比起前一个做了几个改造:
- 把整个代码封装在一个类中
- 为了避免出现同时多次下载同一幅图的问题,使用了本地缓存
封装的类:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
package ghj1976.AndroidTest;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;public class AsyncImageLoader3 { // 为了加快速度,在内存中开启缓存(主要应用于重复图片较多时,或者同一个图片要多次被访问,比如在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 * 回调接口 * <a href="\"http://www.eoeandroid.com/home.php?mod=space&uid=7300\"" target="\"_blank\"">@return</a> 返回内存中缓存的图像,第一次加载返回null */ public Drawable loadDrawable(final String imageUrl, final ImageCallback callback) { // 如果缓存过就从缓存中取出数据 if (imageCache.containsKey(imageUrl)) { SoftReference<Drawable> softReference = imageCache.get(imageUrl); if (softReference.get() != null) { return softReference.get(); } } // 缓存中没有图像,则从网络上取出数据,并将取出的数据缓存到内存中 executorService.submit(new Runnable() { public void run() { try { 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类型时,你可以读取使用该参数,但是无法改变该参数的值。参看:Java关键字final、static使用总结
这里使用SoftReference 是为了解决内存不足的错误(OutOfMemoryError)的,更详细的可以参看:内存优化的两个类:SoftReference 和 WeakReference
前段调用:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
package ghj1976.AndroidTest;import android.app.Activity;import android.graphics.drawable.Drawable;import android.os.Bundle;import android.widget.ImageView;public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); loadImage4("http://www.baidu.com/img/baidu_logo.gif", R.id.imageView1); loadImage4("http://www.chinatelecom.com.cn/images/logo_new.gif", R.id.imageView2); loadImage4("http://cache.soso.com/30d/img/web/logo.gif", R.id.imageView3); loadImage4("http://csdnimg.cn/www/images/csdnindex_logo.gif", R.id.imageView4); loadImage4("http://images.cnblogs.com/logo_small.gif", R.id.imageView5); } private AsyncImageLoader3 asyncImageLoader3 = new AsyncImageLoader3(); // 引入线程池,并引入内存缓存功能,并对外部调用封装了接口,简化调用过程 private void loadImage4(final String url, final int id) { // 如果缓存过就会从缓存中取出图像,ImageCallback接口中方法也不会被执行 Drawable cacheImage = asyncImageLoader3.loadDrawable(url, new AsyncImageLoader3.ImageCallback() { // 请参见实现:如果第一次加载url时下面方法会执行 public void imageLoaded(Drawable imageDrawable) { ((ImageView) findViewById(id)) .setImageDrawable(imageDrawable); } }); if (cacheImage != null) { ((ImageView) findViewById(id)).setImageDrawable(cacheImage); } }} |
本文转自:http://www.eoeandroid.com/thread-210082-1-1.html
【转】[Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式的更多相关文章
- [Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
android线程池的理解,晚上在家无事 预习了一下android异步加载的例子,也学习到了一个很重要的东东 那就是线程池+缓存 下面看他们的理解. [size=1.8em]Handler+Runn ...
- Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
android线程池的理解,晚上在家无事 预习了一下android异步加载的例子,也学习到了一个很重要的东东 那就是线程池+缓存 下面看他们的理解.[size=1.8em]Handler+Runna ...
- 【转】Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式
http://www.cnblogs.com/wanqieddy/archive/2013/09/06/3305482.html android线程池的理解,晚上在家无事 预习了一下android异步 ...
- Android线程管理之ExecutorService线程池
前言: 上篇学习了线程Thread的使用,今天来学习一下线程池ExecutorService. 线程管理相关文章地址: Android线程管理之Thread使用总结 Android线程管理之Execu ...
- java android ExecutorService 线程池解析
ExecutorService: 它也是一个接口,它扩展自Executor接口,Executor接口更像一个抽象的命令模式,仅有一个方法:execute(runnable);Executor接口简单, ...
- Android学习笔记之ExecutorService线程池的应用....
PS:转眼间就开学了...都不知道这个假期到底是怎么过去的.... 学习内容: ExecutorService线程池的应用... 1.如何创建线程池... 2.调用线程池的方法,获取线程执行完毕后的结 ...
- ExecutorService线程池
ExecutorService 建立多线程的步骤: 1.定义线程类 class Handler implements Runnable{} 2.建立ExecutorService线程池 Executo ...
- ExecutorService线程池讲解
ExecutorService 建立多线程的步骤: 1.定义线程类 class Handler implements Runnable{ } 2.建立ExecutorService线程池 Execut ...
- Executor(一)ExecutorService 线程池
Executor(一)ExecutorService 线程池 本篇主要涉及到的是 java.util.concurrent 包中的 ExecutorService.ExecutorService 就是 ...
随机推荐
- SQL Server Compact免安装部署
原文:SQL Server Compact免安装部署 情况 应用程序中的EF使用了SQL Server Compact,打包部署到客户机器上后提示数据库连接异常,信息类似”配置节“.”Provider ...
- 最锋利的Visual Studio Web开发工具扩展:Web Essentials详解
原文:最锋利的Visual Studio Web开发工具扩展:Web Essentials详解 Web Essentials是目前为止见过的最好用的VS扩展工具了,具体功能请待我一一道来. 首先,从E ...
- C#文件操作基础之File类和FileInfo类
文件和I/O流的差异: 文件是一些具有永久存储及特定顺序的字节组成的一个有序的.具有名称的集合. 因此对于文件,我们经常想到文件夹路径,磁盘存储,文件和文件夹名等方面. I/O流提供一种后备存储写入字 ...
- HDOJ 3516 Tree Construction
四边形优化DP Tree Construction Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Jav ...
- 后台使用oracle前台使用easyui分页机制
前台easyui 的datagrid中设置分页属性: pagination:true,//显示分页 pagePosition:'bottom',//分页栏位置 both 上下 bottom.top p ...
- Visual Studio跨平台开发实战(1) - Hello Xamarin!
原文 Visual Studio跨平台开发实战(1) - Hello Xamarin! 前言 应用程式发展的脚步, 从来没有停过. 从早期的Windows 应用程式, 到网路时代的web 应用程式, ...
- ostringstream使用
ostringstream使用 [本文来源于]http://www.builder.com.cn/2003/0304/83250.shtml http://www.cppblog.com/alanto ...
- BZOJ 1269 文本编辑器 Splay
题目大意:维护一个文本编辑器,支持下列操作: 1.将光标移动到某一位置 2.在光标后插入一段字符串 3.删除光标后的一段字符 4.翻转光标后的一段字符 5.输出光标后的一个字符 6.光标-- 7.光标 ...
- 解决.net的堆碎片化带来的内存占用过大的问题
场景 使用WCF开发的服务端在多个客户端登录后,其服务器的内存占用不断增加. 分析 使用Windbg分析得到内存碎片化严重,其中包含了非常大的空闲空间,最大的一块竟然有150M,真正使用的空间其实很小 ...
- 数学思想方法-分布式计算-linux/unix技术基础(5)
shell命令行参数 -bash-4.2$ cat test1.sh#!/bin/shecho "$0 "echo "$1 "echo "$2 ...