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模式+缓存模式的更多相关文章

  1. [Android实例] Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式

    android线程池的理解,晚上在家无事 预习了一下android异步加载的例子,也学习到了一个很重要的东东 那就是线程池+缓存  下面看他们的理解. [size=1.8em]Handler+Runn ...

  2. Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式

    android线程池的理解,晚上在家无事 预习了一下android异步加载的例子,也学习到了一个很重要的东东 那就是线程池+缓存  下面看他们的理解.[size=1.8em]Handler+Runna ...

  3. 【转】Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式

    http://www.cnblogs.com/wanqieddy/archive/2013/09/06/3305482.html android线程池的理解,晚上在家无事 预习了一下android异步 ...

  4. Android线程管理之ExecutorService线程池

    前言: 上篇学习了线程Thread的使用,今天来学习一下线程池ExecutorService. 线程管理相关文章地址: Android线程管理之Thread使用总结 Android线程管理之Execu ...

  5. java android ExecutorService 线程池解析

    ExecutorService: 它也是一个接口,它扩展自Executor接口,Executor接口更像一个抽象的命令模式,仅有一个方法:execute(runnable);Executor接口简单, ...

  6. Android学习笔记之ExecutorService线程池的应用....

    PS:转眼间就开学了...都不知道这个假期到底是怎么过去的.... 学习内容: ExecutorService线程池的应用... 1.如何创建线程池... 2.调用线程池的方法,获取线程执行完毕后的结 ...

  7. ExecutorService线程池

    ExecutorService 建立多线程的步骤: 1.定义线程类 class Handler implements Runnable{} 2.建立ExecutorService线程池 Executo ...

  8. ExecutorService线程池讲解

    ExecutorService 建立多线程的步骤: 1.定义线程类 class Handler implements Runnable{ } 2.建立ExecutorService线程池 Execut ...

  9. Executor(一)ExecutorService 线程池

    Executor(一)ExecutorService 线程池 本篇主要涉及到的是 java.util.concurrent 包中的 ExecutorService.ExecutorService 就是 ...

随机推荐

  1. poj3126解题报告

    题意:简单的说就是:有一个人门牌号是一个四位数的整数,并且还是一个素数,现在他想要换成另外一个四位数且是素数的门牌号,而且,每次都只能更换这个四位数的一个位置的数 ,也就是每换一次都只改变一个数字,而 ...

  2. asp.net Form 认证【转】

    第一部分 如何运用 Form 表单认证 一.        新建一个测试项目 为了更好说明,有必要新建一个测试项目(暂且为“FormTest”吧),包含三张页面足矣(Default.aspx.Logi ...

  3. ECSHOP分类页面筛选功能(按分类下子分类和品牌筛选)

    其实分类页面里面本来就有相关的品牌.属性.分类的筛选功能在category.php和模板加上相应的功能即可 1.读出当前分类的所有下级分类 $chlidren_category = $GLOBALS[ ...

  4. 在配置文件(.settings、.config)中存储自定义对象

    原文:在配置文件(.settings..config)中存储自定义对象 引言 我前面曾写过一篇<使用配置文件(.settings..config)存储应用程序配置>,我在其中指出“sett ...

  5. POJ 1205 Water Treatment Plants(递推)

    题意   建设一条河岸的污水处理系统  河岸有n个城市   每一个城市都能够自己处理污水 V   也能够把污水传到相邻的城市处理 >或<   除了你传给我我也传给你这样的情况   其他都是 ...

  6. windows phone (18) Border元素

    原文:windows phone (18) Border元素 Border类是对某一个对象的周围边框,背景,或者同时绘制两者,首先看一个简单的例子进行分析[作者:神舟龍] xaml文件: <!- ...

  7. Galaxy (hdu 5073 数学)

    Galaxy Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Total S ...

  8. 王立平--Unity综上所述控制

    GUILayout   Label 创建一个自己主动布局的标签. Box 创建一个自己主动布局的box. Button 创建一个单次button.当用户点击button会马上发生一些事件. Repea ...

  9. 谈谈Linux内存释放

    上上周吧,一个朋友问我说他公司的服务器内存free 为0 是为什么,意思大概是内存去哪了,这引发了一个小小的讨论,也就是内存释放的问题… 首先我们可能会用free 去查看内存的使用率,它应该是这样的 ...

  10. Python编程预约参观北京行动纲要

    通过Python程序来模拟一个统一平台预约参观北京,包含验证码识别.登陆.据医院.时间.有关主管部门号等查询. 此程序仅供学习使用,请勿用于其他用途. 1.验证码图片 def getCodePic() ...