http://www.cnblogs.com/wanqieddy/archive/2013/09/06/3305482.html

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(<div style="width: 100px; height: 100px;background: url(static/image/common/loading.gif) no-repeat center center;"></div><img id="\"aimg_LOPt3\"" 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="\"\"" src="\"http://www.chinatelecom.com.cn/images/logo_new.gif\"" lazyloaded="true" style="height: 1px; width: 1px;">",
                                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);
                }
        }
}

 

【转】Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式相关推荐

  1. ExecutorService线程池

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

  2. Java ExecutorService 线程池

    //==采用Java 5的ExecutorService来进行线程池的方式实现多线程,模拟客户端多用户向同一服务器端发送请求. //==服务器端: import java.io.BufferedRea ...

  3. Executors源码解读——创建ExecutorService线程池

    Executors源码解读--创建ExecutorService线程池 〇.[源码版本] jdk 1.8 一.线程池概述 二.线程池创建 三.Executors源码解读 newFixedThreadP ...

  4. 在使用线程池等会缓存线程的组件情况下传递ThreadLocal

    最近开发的系统有一个场景在多个RPC调用链中需要传递一些公有参数过去. 这个时候首先想到的是ThreadLocal,但是有一个问题就是它不能在父子线程中传递上下文信息(ThreadLocal变量信息) ...

  5. SpringBoot - 使用ExecutorService线程池执行异步任务教程(以Runnable任务为例)

    有时我们的系统需要进行一些比较耗时的操作,比如用户注册后要调用邮件服务器给用户发送个邮件,又比如上传一个大数据量的 excel 并导入到数据库.如果后端的这些工作比较耗时,那么前台的页面便会一直处于等 ...

  6. java线程池实现生产者消费者模式

    本人并发小白,如果有可以优化的地方欢迎评论交流- 题目如下: ​  15个生产者生产1000条数据(从1开始,每生产一条加1),并向消息队列中存放字符串("第m个生产者生产数据n" ...

  7. [转]new Thread的弊端及Java四种线程池的使用

    介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端 执行一个异步任务你还只是如下new ...

  8. 自定义线程池内置线程池的使用 ThreadPoolExecutor和Executorservice 示例与注意事项

    文章目录 线程池介绍 自己设计一个线程池 1.设计ThreadPool类: 2.设计工作队列 3.实现自己设计的线程池 用java的ThreadPoolExecutor自定义线程池 自定义线程池-参数 ...

  9. 浅析Java线程池 ExecutorService

    ExecutorService是Java中对线程池定义的一个接口,它java.util.concurrent包中. 创建一个什么样的ExecutorService的实例(即线程池)需要g根据具体应用场 ...

  10. 火遍全网的Hutool,如何使用Builder模式构建线程池

    前言 Builder 设计模式也叫做 构建者模式或者建造者模式,名字只是一种叫法,当聊起三种名称的时候知道是怎么回事就行 Builder 设计模式在作者编码过程中,属于比较常用的模式之一.优秀的设计模 ...

最新文章

  1. Codeforces Round #641 (Div. 2)A~E题解(数论场)
  2. C++ 强制类型转换(转载)
  3. android byteBuffer的使用
  4. android singleinstance home,Android启动模式之singleinstance的坑
  5. 排序算法中平均时间复杂度_操作系统中的作业排序(算法,时间复杂度和示例)...
  6. python 社区网络转化_python-将numpy打开网格转换为坐标
  7. 一支笔卖70万,为的是能打开笔,复杂的像辆车,英国女王都用它
  8. 【Flink】Unable to retrieve any partitions with KafkaTopicsDescriptor: Fixed Topics ([xxx)]
  9. List 中的元素排序
  10. mac 2k显示器字体发虚_亲身实践:Mac mini 搭配戴尔显示器体验
  11. Atitit it系列书籍列表 C:\Users\Administrator\Documents\it 软件系列书籍\itlist.txt C:\Users\Administrator\Docume
  12. 电压型传感器和电流型传感器的区别
  13. java后台通过http请求下载文件
  14. 前端element-ui中图片oss直传到阿里云
  15. 删除word标题前方空格的方法
  16. MySQL实现分数排名问题
  17. iphone进入安全模式及退出安全模式的方法
  18. 学术资源不定期分享-【费曼物理学讲义英文原版】
  19. java swing(GUI图形化界面)基础教程3-添加事件监听1
  20. C++ 修改防火墙firewall设置(Linux、Ubuntu、CentOS)

热门文章

  1. 重置SQLSERVER表的自增列,让自增列重新计数【转】
  2. 在Windows Mobile上隐藏你的应用程序
  3. linux进程互斥等待
  4. 剑指offer---数组中重复的数字
  5. Vue学习笔记之Nodejs中的NPM使用
  6. Flexbox 布局教程
  7. 《大型网站技术架构:核心原理与案例分析》阅读笔记四
  8. C#预处理器指令【转】
  9. C# 不借助第三个变量实现两整数交换
  10. Delphi中ListView和TreeView的Item中的内存泄露