【转】Handler学习笔记(二)
一.一个问题
有这样一个问题值得我们思考,若把一些类似于下载的功能(既耗时且不一定有结果)写在Activity(主线程)里,会导致Activity阻塞,长时间无响应,直至页面假死(如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 "强制关闭")。因此,我们需要把这些耗时的操作放在单独的子线程中操作。这就是Handler的使命。Handler提供异步处理的功能,发送和接收不是同时的(Activity的主线程和线程队列里的线程是不同的线程,并行进行,互不影响)。
二.Handler简介
Handler 为Android操作系统中的线程通信工具,它主要由两个作用:(1)安排消息或Runnable 在某个主线程中某个地方执行(2)安排一个动作在另外的线程中执行。每个Handler对象维护两个队列(FIFO),消息队列和Runnable队列,都是有Android操作系统提供的。Handler可以通过这两个队列来分别:
- 发送、接受、处理消息–消息队列;
- 启动、结束、休眠线程–Runnable队列;
Handler的使用方法大体分为3个步骤:1.创建Handler对象。2.创建Runnable和消息。3.调用post以及sendMessage方法将Runnable和消息添加到队列。
三.Runnable队列
1.java中的线程
在java中,线程的创建有两种方法:继承Thread类和实现Runnable接口。而这最重要的都是要复写run方法来实现线程的功能。当线程的时间片到了,开始运行时,就执行run()函数,执行完毕,就进入死亡状态。
举个创建线程的例子:
Runnable r=new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("thread");
handler.postDelayed(thread, 3000);
}
};
2.关于Runnable队列
(1)原理
Android的线程异步处理机制:Handler对象维护一个线程队列,有新的Runnable送来(post())的时候,把它放在队尾,而处理Runnable的时候,从队头取出Runnable执行。当向队列发送一个Runnable后,立即就返回,并不理会Runnable是否被执行,执行是否成功等。而具体的执行则是当排队排到该Runnable后系统拿来执行的。这就好比邮局的例子。寄信者将信写好后放入邮筒就回家了,他并不知道邮件何时被邮局分发,何时寄到,对方怎样读取这些事。这样,就实现了Android的异步处理机制。
(2)具体操作
向队列添加线程:
handler.post(Runnable );将Runnable直接添加入队列
handler.postDelayed(Runnable, long)延迟一定时间后,将Runnable添加入队列
handler.postAtTime(Runnable,long)定时将Runnable添加入队列
终止线程:
handler.removeCallbacks(thread);将Runnable从Runnable队列中取出
四.消息队列
1.消息对象
(1)Message对象
Message对象携带数据,通常它用arg1,arg2来传递消息,当然它还可以有obj参数,可以携带Bundle数据。它的特点是系统性能消耗非常少。
初始化: Message msg=handler.obtainMessage();
(2)Bundle对象
Bundle是Android提供的类,可以把它看做是特殊的Map,即键值对的包。而它特殊在键和值都必须要是基本数据类型或是基本数据类型的数组(Map的键值要求都是对象),特别的,键要求都是String类型。用Message来携带Bundle数据:
放入:msg.setData(Bundle bundle);
取出:msg.getData();
2.关于消息队列
(1)原理
Android的消息异步处理机制:Handler对象维护一个消息队列,有新的消息送来(sendMessage())的时候,把它放在队尾,之后排队到处理该消息的时候,由主线程的Handler对象处理(handleMessage())。整个过程也是异步的,和Runnable队列的原理相同。
(2)具体操作:
向队列添加Runnable:
handler.sendMessage(Message);将消息发送到消息队列
msg.sendToTarget();同上
handler.sendMessageDelayed(Message,long);延迟一定时间后,将消息发送到消息队列
handler.sendMessageAtTime(Message,long)定时将消息发送到消息队列
msg.sendToTarget();
处理消息:
消息的具体处理过程,需要在new Handler对象时使用匿名内部类重写Handler的handleMessage(Message msg)方法,如下:
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
。。。。。。
。。。。。。
}
};
五.Handler的两个作用
1.安排消息或Runnable 在某个主线程中某个地方执行
代码示例:
public class HandlerTestActivity extends Activity {
private Button start;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.handlertest);
start=(Button) findViewById(R.id.start);
start.setOnClickListener(new startListener());
System.out.println("Activity Thread:"+Thread.currentThread().getId());
}
Handler handler=new Handler();
Runnable thread=new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("HandlerThread:"+Thread.currentThread().getId());
}
};
class startListener implements OnClickListener{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
handler.post(thread);
}
}
}
这个小程序中,首先程序启动,进入onCreate(),打印出当前线程(即主线程)的ID,之后点击按钮start,会将线程thread添加到线程队列,执行线程thread,thread的作用就是打印出当前线程的ID。在这个程序中,我们可以看到通过Handler我们可以实现安排Runnable 在某个主线程中某个地方执行,即作用(1)。
不过这里有个小小的陷阱,你发现了吗?这个程序看上去似乎实现了Handler的异步机制, handler.post(thread)似乎实现了新启线程的作用,不过通过执行我们发现,两个线程的ID相同!也就是说,实际上thread还是原来的主线程,由此可见,handler.post()方法并未真正新建线程,只是在原线程上执行而已,我们并未实现异步机制。
2.安排一个动作在另外的线程中执行。
(1)java中标准的创建线程的方法
第一步:
Runnable r=new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("thread");
handler.postDelayed(thread, 3000);
}
};
第二步:
Thread t=new Thread (r);
第三步:
t.start();
若把上面示例程序中的handler.post(thread);语句改成以上形式,通过打印我们可以看到,两个ID是不同的,新的线程启动了!
(2)关于Looper
Looper类用来为线程开启一个消息循环,作用是可以循环的从消息队列读取消息,所以Looper实际上就是消息队列+消息循环的封装。每个线程只能对应一个Looper,除主线程外,Android中的线程默认是没有开启Looper的。
通过Handler与Looper交互,Handler可以看做是Looper的接口,用来向指定的Looper发送消息以及定义处理方法。默认情况下Handler会与其所在线程的Looper绑定,即:
Handler handler=new Handler();等价于Handler handler=new Handler(Looper.myLooper());
Looper有两个主要方法:
Looper.prepare();启用Looper
Looper.loop(); 让Looper开始工作,从消息队列里取消息,处理消息。
注意:写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。
(3)Handler异步机制的实现
Handler是通过HandlerThread 使得子线程与主线程分属不同线程的。实际上,HandlerThread 是一个特殊的线程,它是一个封装好Looper的线程,
代码示例:
//创建一个名叫handler_hread的HandlerThread 对象
HandlerThread handlerThread=new HandlerThread("handler_hread");
//开启handlerThread,在使用handlerThread.getLooper()之前必须先调用start方法,否则取出的是空
handlerThread.start();
//将handler绑定在handlerThread的Looper上,即这个handler是运行在handlerThread线程中的
myHandler handler=new myHandler(handlerThread.getLooper());
class myHandler extends Handler{
public myHandler(){}
public myHandler(Looper looper){
super(looper);
}
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
System.out.println("Activity Thread:"+Thread.currentThread().getId());
}
}
这样,就实现了handler的异步处理机制,在调用handler.post()方法,通过打印线程ID可以得知,子线程与主线程是分属不同线程的。
【转】Handler学习笔记(二)的更多相关文章
- muduo学习笔记(二)Reactor关键结构
目录 muduo学习笔记(二)Reactor关键结构 Reactor简述 什么是Reactor Reactor模型的优缺点 poll简述 poll使用样例 muduo Reactor关键结构 Chan ...
- WPF的Binding学习笔记(二)
原文: http://www.cnblogs.com/pasoraku/archive/2012/10/25/2738428.htmlWPF的Binding学习笔记(二) 上次学了点点Binding的 ...
- AJax 学习笔记二(onreadystatechange的作用)
AJax 学习笔记二(onreadystatechange的作用) 当发送一个请求后,客户端无法确定什么时候会完成这个请求,所以需要用事件机制来捕获请求的状态XMLHttpRequest对象提供了on ...
- [Firefly引擎][学习笔记二][已完结]卡牌游戏开发模型的设计
源地址:http://bbs.9miao.com/thread-44603-1-1.html 在此补充一下Socket的验证机制:socket登陆验证.会采用session会话超时的机制做心跳接口验证 ...
- JMX学习笔记(二)-Notification
Notification通知,也可理解为消息,有通知,必然有发送通知的广播,JMX这里采用了一种订阅的方式,类似于观察者模式,注册一个观察者到广播里,当有通知时,广播通过调用观察者,逐一通知. 这里写 ...
- java之jvm学习笔记二(类装载器的体系结构)
java的class只在需要的时候才内转载入内存,并由java虚拟机的执行引擎来执行,而执行引擎从总的来说主要的执行方式分为四种, 第一种,一次性解释代码,也就是当字节码转载到内存后,每次需要都会重新 ...
- Java IO学习笔记二
Java IO学习笔记二 流的概念 在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成. 程序中的输入输 ...
- 《SQL必知必会》学习笔记二)
<SQL必知必会>学习笔记(二) 咱们接着上一篇的内容继续.这一篇主要回顾子查询,联合查询,复制表这三类内容. 上一部分基本上都是简单的Select查询,即从单个数据库表中检索数据的单条语 ...
- NumPy学习笔记 二
NumPy学习笔记 二 <NumPy学习笔记>系列将记录学习NumPy过程中的动手笔记,前期的参考书是<Python数据分析基础教程 NumPy学习指南>第二版.<数学分 ...
- Learning ROS for Robotics Programming Second Edition学习笔记(二) indigo tools
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...
随机推荐
- poj3278 Catch That Cow
Catch That Cow Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 73973 Accepted: 23308 ...
- bzoj1036 [ZJOI2008]树的统计Count
1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MB Submit: 12646 Solved: 5085 [Subm ...
- javascript删除元素节点
1.删除元素父节点 function removeElement(_element){ var _parentElement = _element.parentNode; if(_parentElem ...
- nginx利用geo模块做限速白名单以及geo实现全局负载均衡的操作记录
geo指令使用ngx_http_geo_module模块提供的.默认情况下,nginx有加载这个模块,除非人为的 --without-http_geo_module.ngx_http_geo_modu ...
- html之marquee详解
该标签不是HTML3.2的一部分,并且只支持MSIE3以后内核,所以如果你使用非IE内核浏览器(如:Netscape)可能无法看到下面一些很有意思的效果该标签是个容器标签语法: <marquee ...
- 详细学习ORACLE JOBS
一点一点学习jobs的各个方面比较长,比较烦,但是应该看完后会对jobs比较好的应用 一.学习准备 开始dbms_job学习前,先认识一个参数job_queue_processes a.job_que ...
- ejb3: message drive bean(MDB)示例
上一篇已经知道了JMS的基本操作,今天来看一下ejb3中的一种重要bean:Message Drive Bean(mdb) 如果要不断监听一个队列中的消息,通常我们需要写一个监听程序,这需要一定的开发 ...
- c++返回值 注意事项
1.不要返回指向局部变量或临时对象的引用.函数执行完毕后,局部变量和临时对象会消失,引用将指向不存在的数据 2.返回指向const对象的引用 使用const引用的常见原因是旨在提高效率,但对于何时采用 ...
- <实训|第六天>偷偷让新手的Linux无限重启附linux主机名称不是随便乱改的!
先说个事情:这几天我正在忙一个项目的设计,8月1号之前要弄出来,所以每天都要弄到很晚,可能更新就有点跟不上了,不过我如果有时间的话,我就更新,没时间的话,我会在8月1号之后统一更新出来,希望大家谅解! ...
- 怎样修改 Openstack Horizon(Dashboard)的显示界面 (一)
Openstack 有很多项目,比如 nova 是虚拟机管理,neutron 是虚拟网络管理, glance 是存储管理,而 horizon 是负责 Openstack 的统一界面.horizon 的 ...