from : http://www.cnblogs.com/bastard/archive/2012/06/08/2541944.html
作为Android中大量使用的Handler,结合Thread使其具有众多的使用形式和方法,
让我一时感觉这个东西有些玄乎,不明所以然,这到底是一个什么样的存在呢?通过网上
资料和源码的学习,这个Handler也差不多弄清楚了,现在总结下这个学习结果。
一 Handler作用和概念
通过官方文档了解到Handler的大致概念是:
Handler能够让你发送和处理消息,以及Runnable对象;每个Handler对象对应一个Thread和
Thread的消息队列。当你创建一个Handler时,它就和Thread的消息队列绑定在一起,然后就可以
传递消息和runnable对象到消息队列中,执行消息后就从消息队列中退出。
Handler的作用就是:调度消息和runnable对象去被执行;使动作在不同的线程中被执行。
当一个应用程序中进程被创建时,它的主线程专门运行消息队列(messageQueue),去管
理顶层的应用程序相关的对象如:activity,broadcastReceiver,windows等,你可以创建你
的Thread,和主线程进行交互——通过Handler,交互的方法就是通过post或者sendMessage。
但是在你的新线程中,给定的Message或者Runnable,会在适当的时候的被调度和处理。
(即不会被立即处理——阻塞式)。
这是官方文档中对Handler描述的大致意思(英文比较烂翻译不定正确)。
从这些文档中我们大概了解到handler干了些什么:
- 运行在某个线程上,共享线程的消息队列;
- 接收消息、调度消息,派发消息和处理消息;
- 实现消息的异步处理;
基本上就是和消息有关,那么这实际上是在干什么呢?
——建立消息处理模型/系统。
要学习Handler,看到肯定是和消息有关,可能还是需要先熟悉一下消息系统的构成和简单原理。
下面就先学习一下消息系统的基本原理。
二 消息系统的基本原理和构成
从一般的消息系统模型的建立大致构成以下几个部分:
l 消息原型
l 消息队列
l 发送消息
l 消息循环
l 消息获取
l 消息派发
l 消息处理
大致模型图如下:
消息系统模型一般会包括以上七个部分(消息原型,消息队列,消息发送,消息循环,消息获取,
消息派发,消息处理)。实际上的核心是消息队列和消息循环,其余部分都是围绕这两部分进行的。
从前面文档的分析中我们知道Handler就是用来建立消息处理的系统模型,那么和这里基本消息
系统模型相比,那么Handler又是如何囊括这七个部分的呢?
在Android中对这六个部分进行了抽象成四个独立的部分:
Handler,Message,MessageQueue,Looper;
- Message就是消息原型,包含消息描述和数据,
- MessageQueue就是消息队列,
- Looper完成消息循环
- Handler就是驾驭整个消息系统模型,统领Message,MessgeQueue和Looper;
Handler能够实现消息系统模型,那么具体是如何进行工作的呢,下面探究一下这其中工作的方法和原理。
三 Handler工作原理分析
要了解Handler工作原理,先看一下这个系统模型具体组成的层次结构框架是个什么样的。
Looper:
实现Thread的消息循环和消息派发,缺省情况下Thread是没有这个消息循环的既没有Looper;
需要主动去创建,然后启动Looper的消息循环loop;与外部的交互通过Handler进行;
MessageQueue:
消息队列,由Looper所持有,但是消息的添加是通过Handler进行;
消息循环和消息队列都是属于Thread,而Handler本身并不具有Looper和MessageQueue;
但是消息系统的建立和交互,是Thread将Looper和MessageQueue交给某个Handler维护建立消息系统模型。
所以消息系统模型的核心就是Looper。消息循环和消息队列都是由Looper建立的,
而建立Handler的关键就是这个Looper。
一个Thread同时可以对应多个Handler,一个Handler同时只能属于一个Thread。Handler属于哪个
Thread取决于Handler在那个Thread中建立。
在一个Thread中Looper也是唯一的,一个Thread对应一个Looper,建立Handler的Looper来自哪个Thread,
Handler属于哪个Thread。
故建立Thread消息系统,就是将Thread的Looper交给Handler去打理,实现消息系统模型,完成消息的异步处理。
Handler与Thread及Looper的关系可以用下面图来表示:
Handler并不等于Thread,必须通过Thread的Looper及其MessageQueue,
用来实现Thread消息系统模型,依附于Thread上。
在线程建立Handler时:
使Handler满足消息系统需要的条件,将Thread中的Looper和MessageQueue交给Handler来负责维护。
在线程中建立Handler,需要做以下工作:
l 获取Thread中的Looper交给Handler的成员变量引用维护;
l 通过Looper获取MessageQueue交给Handler的成员变量引用维护。
那么消息系统模型建立完成之后,按照消息系统运行,
从Handler来看就是发送消息派发消息,与此线程消息系统的交互都由Handler完成。
消息发送和派发接口:
l post(runnable)消息,Runnable是消息回调,经过消息循环引发消息回调函数执行;
l sendMessage(Message)消息,经过消息循环派发消息处理函数中处理消息;
l dispatchMessage 派发消息,若是post或带有回调函数则执行回调函数,否则执行
消息处理函数Handler的handleMessage(通常派生类重写)。
以上就是Handler如何实现Thread消息系统模型的大致介绍。
下面将具体分析是如何实现消息系统模型运行的。
四 Handler实现流程分析
我们知道Handler就是一个消息系统的外壳,属于某个Thread并包装了Thread的Looper
及其MessageQueue;与外部进行交互(同一个线程内或者线程之间),接收派发和处理消息,
消息系统模型的核心是Looper。
下面看看Handler是如何建立跑起来的,以msg消息为例,runnable实质是一样。
1 Handler的建立
Handler唯一属于某个Thread,在某个Thread中建立Handler时,需要获取Thread的Looper
及其MessageQueue,建立Handler关键是Looper的来源。
Handler提供了好几个构造函数但其本质一致:
由外部传入Looper:当前线程或其他线程
public Handler(Looper looper) { //初始化构建消息系统参数 mLooper = looper; mQueue = looper.mQueue; mCallback = null; }
从当前线程获取:由创建Handler的Thread决定
public Handler() { //初始化构建消息系统参数 mLooper = Looper.myLooper(); mQueue = mLooper.mQueue; mCallback = null; } public static Looper myLooper() { return sThreadLocal.get(); }
不管哪种方式,我们知道Thread在默认情况下是没有建立消息循环Looper实例的。
要实现消息循环必须确保Thread的Looper建立。如何确保呢?
Looper提供了静态函数:
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
//存储线程的局部变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
看到这里刚开始让我很是奇怪和迷惑:
Looper一个独立的类,又不属于某个Thread,而这里创建Looper的函数又是静态的,
属于整个Looper类;创建Looper之后交给静态成员变量sThreadLocal保存,获取
sThreadLocal.get(),那么一个静态变量属于整个类,属性更改始终有效。一次创建之后
sThreadLocal.get()永远都不等于null!
而Thread和Looper是唯一对应的,那这里岂不是所有的Thread都是用同一个Looper,不可能!
所以肯定这个ThreadLocal是有玄机的。网上一查:
ThreadLocal:
维护线程的变量,为每个使用该变量的线程实例提供独立的变量副本,每个线程都能够独立使用该变量,
而互不影响。(详细可参考:http://blog.csdn.net/qjyong/article/details/2158097)
所以每一个线程调用Looper.prepare时,都会创建为其唯一的Looper。
要建立Handler,需要先创建线程的Looper,才能建立消息系统模型。通过Looper我们建立了
Thread上的消息系统模型Handler,可以来进行消息系统的一系列流程了。
2 消息发送
消息发送两种方式:post和sendMessage;
post:针对runnable对象;Runnable是一个接口,就是一个回调函数(提供了run方法)
sendMessage:针对Message对象;
下面通过代码具体看一下这个过程:
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
看到post和sendMessage发送消息时,仅仅是对象不同而已,Runnable和Message;
但实际上都是Message的形式来描述。
这跟我通常理解的消息机制不同:
通常post消息是将消息加入到消息队列中并不立即执行就返回,send消息是立即执行等待消息执行完才返回。
而这里post或者send都是将消息放入到消息队列中,然后立即返回,等待消息循环时获取消息被执行。
这里提供了众多的消息发送方法来指定消息的执行时间和顺序,具体可以查看源代码。
消息执行顺序是根据消息队列中消息的排列顺序而定。
下面看一下发送消息后将消息加入到消息队列中的代码:
由Handler调用MessageQueue的enqueueMessage方法:
final boolean enqueueMessage(Message msg, long when) { Message p = mMessages; if (p == null || when == 0 || when < p.when) { msg.next = p; mMessages = msg; } else { Message prev = null; while (p != null && p.when <= when) { prev = p; p = p.next; } msg.next = prev.next; prev.next = msg; } …… }
可以看到是按照时间顺序将消息加入到MessageQueue中;
现在将消息加入到消息队列中存储起来,消息并未得到处理,下一步必然是如何派发消息和处理消息。
3 消息派发
建立Thread消息循环由Looper完成,存在一个消息调度死循环:
public static void loop() { MessageQueue queue = me.mQueue; while (true) { Message msg = queue.next(); // might block if (msg != null) { if (msg.target == null) { // No target is a magic identifier for the quit message. return; } //派发消息 到target(Handler) msg.target.dispatchMessage(msg); //回收Msg到msgPool msg.recycle(); } } }
这里看到消息派发是由Message的target完成,这个target是什么呢?是一个Handler。
消息系统是通过Handler用来与外部交互,把消息派发出去。可以看到没有这个Handler,消息循环将结束。
消息派发由Looper通过Handler完成:
public void dispatchMessage(Message msg) {
//首先判断runnable对象
if (msg.callback != null) {
handleCallback(msg);
}
else {
//整个消息系统的回调函数 可以不用实现自己Handler
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//消息处理 通常交给Handler派生类
handleMessage(msg);
}
}
通过消息派发,这样就实现消息的异步处理。
4 消息原型
前面看到消息发送有两种方式:
post(Runnable对象),sendMessage(Message对象),而中间都是通过Message对象
保存在MessageQueue中。然后消息派发时处理方式不同。如果在sendMessage时将将消息对象
附上Runnable对象,则post和sendMessage没有区别了。所以这两种方式很好理解基本一致,处理的方式不同罢了。
消息系统模型中,我们的真正的消息原型是什么,都具有那些功能,下面看一下Message中到底
包含了那些东西,能有效帮助我们合理的运用消息系统来完成一些任务和处理。
Message消息原型:
public final class Message implements Parcelable { //标识消息 public int what; int flags; long when; //传递简单数据 public int arg1; public int arg2; //传递较复杂数据 对象 public Object obj; Bundle data; //处理消息的目标Handler Handler target; //消息派发时 执行的Runnable对象 Runnable callback; //使消息形成链表 Message next; //建立一个消息pool,回收msg,以避免重复创建节约开销 private static Message sPool; private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 10; }
看到提供了很丰富的属性来描述消息,针对具体问题选择使用那些属性去怎么样描述消息了。
获取新的Message对象时,Message提供了obtain方法:避免我们自己去分配Message新的对象,
通过obtain获取,可能从MessagePool中获取,节约开销。
下面看一下这个MessagePool是如何建立的:
通常消息处理完毕的时候,消息也基本上处于无用状态可以释放回收了。对于需要频繁的创建释放的对象来说,
创建和释放类实例都是要开销的,太频繁的使开销增大不好,像Message这种很有可能会频繁的创建。
于是我们可以将创建的对象用完之后保存在一个Pool里面,以便再重复利用节约频繁创建释放开销。
是如何建立的呢?必然是在消息处理完毕之后才能进行。
MessagePool建立:
public static void loop() {
while (true) {
//派发消息
msg.target.dispatchMessage(msg);
//消息处理完毕 回收
msg.recycle();
}
}
public void recycle() {
//回收Message 建立全局的MessagePool
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
五 Handler的应用
以上这就是整个Handler作用及消息系统模型的建立。
使用也非常简单,虽然有很多方式,但只要理解Handler是建立在Looper上,实现Thread的
消息系统处理模型,实现消息异步处理,我想对与Handler基本应用上没有什么不能理解的了。
其他方面可以去看源码了。
Handler使用起来是非常简单的,关键就是如何利用消息的异步处理,来合理的完成我们
需要功能和任务。对于一个Thread,我们使用好几个Handler来进行异步处理,也可以创建新的Thread,
通过Handler来实现消息异步处理等等,应用场景很多如何用的好用的合理,这就没什么经验了。
至于如何使用,源码中很多例子可以看一下AsyncQueryHandler这个类,其中两个线程,
完成查询工作,通过Handler进行线程之间有消息传递。感觉这个利用的很好很巧妙。
参考文章:http://blog.csdn.net/maxleng/article/details/5552976
方法一:(java习惯,在android不推荐使用)
刚刚开始接触android线程编程的时候,习惯好像java一样,试图用下面的代码解决问题
new Thread( new Runnable() { public void run() { myView.invalidate(); } }).start();
可以实现功能,刷新UI界面。但是这样是不行的,因为它违背了单线程模型:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。
方法二:(Thread+Handler)
查阅了文档和apidemo后,发觉常用的方法是利用Handler来实现UI线程的更新的。
Handler来根据接收的消息,处理UI更新。Thread线程发出Handler消息,通知更新UI。
Handler myHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case TestHandler.GUIUPDATEIDENTIFIER: myBounceView.invalidate(); break ; } super .handleMessage(msg); } };
class myThread implements Runnable { public void run() { while ( ! Thread.currentThread().isInterrupted()) { Message message = new Message(); message.what = TestHandler.GUIUPDATEIDENTIFIER; TestHandler. this .myHandler.sendMessage(message); try { Thread.sleep( 100 ); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } }
以上方法demo看:http://rayleung.javaeye.com/blog/411860
方法三:(java习惯,不推荐)
在Android平台中需要反复按周期执行方法可以使用Java上自带的TimerTask类,TimerTask相对于Thread来说对于资源消耗的更低,除了使用Android自带的AlarmManager使用Timer定时器是一种更好的解决方法。 我们需要引入import java.util.Timer; 和 import java.util.TimerTask;
public class JavaTimer extends Activity { Timer timer = new Timer(); TimerTask task = new TimerTask(){ public void run() { setTitle( " hear me? " ); } }; public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.main); timer.schedule(task, 10000 ); } }
方法四:(TimerTask + Handler)
实际上这样做是不行的,这跟Android的线程安全有关!应该通过配合Handler来实现timer功能的!
public class TestTimer extends Activity { Timer timer = new Timer(); Handler handler = new Handler(){ public void handleMessage(Message msg) { switch (msg.what) { case 1 : setTitle( " hear me? " ); break ; } super .handleMessage(msg); } };TimerTask task = new TimerTask(){ public void run() { Message message = new Message(); message.what = 1 ; handler.sendMessage(message); } };public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.main);timer.schedule(task, 10000 ); } }
方法五:( Runnable + Handler.postDelayed(runnable,time) )
在Android里定时更新 UI,通常使用的是 java.util.Timer, java.util.TimerTask, android.os.Handler组合。实际上Handler 自身已经提供了定时的功能。
private Handler handler = new Handler(); private Runnable myRunnable = new Runnable() { public void run() { if (run) { handler.postDelayed( this , 1000 ); count ++ ; } tvCounter.setText( " Count: " + count); } };
然后在其他地方调用
handler.post(myRunnable);
handler.post(myRunnable,time);
案例看:http://shaobin0604.javaeye.com/blog/515820
====================================================================
知识点总结补充:
很多初入Android或Java开发的新手对Thread、Looper、Handler和Message仍然比较迷惑,衍生的有HandlerThread、java.util.concurrent、Task、AsyncTask由于目前市面上的书籍等资料都没有谈到这些问题,今天就这一问题做更系统性的总结。我们创建的Service、Activity以及Broadcast均是一个主线程处理,这里我们可以理解为UI线程。但是在操作一些耗时操作时,比如I/O读写的大文件读写,数据库操作以及网络下载需要很长时间,为了不阻塞用户界面,出现ANR的响应提示窗口,这个时候我们可以考虑使用Thread线程来解决。
对于从事过J2ME开发的程序员来说Thread比较简单,直接匿名创建重写run方法,调用start方法执行即可。或者从Runnable接口继承,但对于Android平台来说UI控件都没有设计成为线程安全类型,所以需要引入一些同步的机制来使其刷新,这点Google在设计Android时倒是参考了下Win32的消息处理机制。
1. 对于线程中的刷新一个View为基类的界面,可以使用postInvalidate()方法在线程中来处理,其中还提供了一些重写方法比如postInvalidate(int left,int top,int right,int bottom) 来刷新一个矩形区域,以及延时执行,比如postInvalidateDelayed(long delayMilliseconds)或postInvalidateDelayed(long delayMilliseconds,int left,int top,int right,int bottom) 方法,其中第一个参数为毫秒
2. 当然推荐的方法是通过一个Handler来处理这些,可以在一个线程的run方法中调用handler对象的 postMessage或sendMessage方法来实现,Android程序内部维护着一个消息队列,会轮训处理这些,如果你是Win32程序员可以很好理解这些消息处理,不过相对于Android来说没有提供 PreTranslateMessage这些干涉内部的方法。
3. Looper又是什么呢? ,其实Android中每一个Thread都跟着一个Looper,Looper可以帮助Thread维护一个消息队列,但是Looper和Handler没有什么关系,我们从开源的代码可以看到Android还提供了一个Thread继承类HanderThread可以帮助我们处理,在HandlerThread对象中可以通过getLooper方法获取一个Looper对象控制句柄,我们可以将其这个Looper对象映射到一个Handler中去来实现一个线程同步机制,Looper对象的执行需要初始化Looper.prepare方法就是昨天我们看到的问题,同时推出时还要释放资源,使用Looper.release方法。
4.Message 在Android是什么呢? 对于Android中Handler可以传递一些内容,通过Bundle对象可以封装String、Integer以及Blob二进制对象,我们通过在线程中使用Handler对象的sendEmptyMessage或sendMessage方法来传递一个Bundle对象到Handler处理器。对于Handler类提供了重写方法handleMessage(Message msg) 来判断,通过msg.what来区分每条信息。将Bundle解包来实现Handler类更新UI线程中的内容实现控件的刷新操作。相关的Handler对象有关消息发送sendXXXX相关方法如下,同时还有postXXXX相关方法,这些和Win32中的道理基本一致,一个为发送后直接返回,一个为处理后才返回 .
5. java.util.concurrent对象分析,对于过去从事Java开发的程序员不会对Concurrent对象感到陌生吧,他是JDK 1.5以后新增的重要特性作为掌上设备,我们不提倡使用该类,考虑到Android为我们已经设计好的Task机制,这里不做过多的赘述,相关原因参考下面的介绍:
6. 在Android中还提供了一种有别于线程的处理方式,就是Task以及AsyncTask,从开源代码中可以看到是针对Concurrent的封装,开发人员可以方便的处理这些异步任务。