Android Handler和HandlerThread
Posted xhmj12
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Handler和HandlerThread相关的知识,希望对你有一定的参考价值。
Handler的官方解释如下:
A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue.Each Handler instance is associated with a single thread and that thread's message queue.
Handler会关联一个单独的线程和消息队列。Handler默认关联主线程,虽然要提供Runnable参数,但默认是直接调用Runnable的run()方法。也就是默认下会在主线程执行,如果在这里面的操作会有阻塞,界面也会卡住。如果要在其他线程执行,可以使用HandlerThread。
Handler的使用方法:
Handler handler = new Handler()
@Override
public void handleMessage(Message msg)
super.handleMessage(msg);
//处理发送过来的消息
Bundle b = msg.getData();
System.out.println("msg:" + msg.arg1);
System.out.println("msg:" + b.getString("name") + " - age:" + b.getInt("age"));
;
HandlerThread使用方法:
//把上面创建Handler的代码
Handler handler = new Handler()
...
//改为:
HandlerThread thread = new HandlerThread("athread");
thread.start(); //要把线程启动
Handler handler = new Handler(thread.getLooper())
...
下面详细讲解下Handler的消息机制
1.相关类:
Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一队列,终由Handler处理。
Handler:处理者,负责Message的发送和处理,使用Hander时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。
当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
Thread:线程,负责调度整个消息循环,即消息循环的执行场所。
Handler,Looper和MessageQueue就是简单的三角关系。Looper和MessageQueue一一对应,创建一个Looper的同时,会创建一个MessageQueue。而Handler与他们的关系,只是简单的聚集关系,即Handler里会引用当前线程里的特定Looper和MessageQueue。
这样说来,多个Handler都可以共享同一Looper和MessageQueue了。当然,这些Handler也就运行在同一个线程里。
接下来,我们简单的看下消息的循环过程:
生成
Message msg = mHandler.obtainMessage();
msg.what = what;
msg.sendToTarget();
发送
MessageQueue queue = mQueue;
if (queue != null)
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
在Handler.java的sendMessageAtTime(Message msg,long uptimeMillis)方法中,我们看到,它找到它所引用的MessageQueue,然后将Message的target设定成自己(目的是为了在处理消息环节,Message能找到正确的Handler),再将这个Message纳入到消息队列中。
抽取
Looper me = myLooper();
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;
msg.target.dispatchMessage(msg);
msg.recycle();
在Looper.java的loop()函数里,我们看到,这里有一个死循环,不断地从MessageQueue中获取下一个(next())Message,然后通过Message中携带的target信息,交由正确的Handler处理(dispatchMessage方法)。
处理
在Looper.java的loop()函数里,我们看到,这里有一个死循环,不断地从MessageQueue中获取下一个(next())Message,然后通过Message中携带的target信息,交由正确的Handler处理(dispatchMessage方法)。
处理
if (msg.callback != null)
handleCallback(msg);
else
if (mCallback != null)
if (mCallback.handleMessage(msg))
return;
handleMessage(msg);
在Handler.java的dispatchMessage(Message msg)方法里,其中的一个分支就是调用handleMessage方法来处理这条Message,而这也正是我们在职责处描述使用Handler时需要实现handleMessage(Message msg)的原因。
至于dispatchMessage方法中的另外一个分支,将在后面的内容中说明。
至此,我们看到,一个Message经由Handler的发送,MessageQueue的入队,Looper的抽取,又再一次地回到Handler的怀抱,而绕的这一圈,也正好帮助我们将同步操作变成了异步操作。
下面,我们讨论下Handler所处的线程及更新UI的方式
在主线程(UI线程)里,如果创建Handler时不传入Looper对象,那么将直接使用主线程(UI线程)的Looper对象(系统已经帮我们创建了);在其他线程里,如果创建Handler时不传入Looper对象,那么,这个Handler将不能接收处理消息。在这种情况下,通用的作法是:
class LooperThread extends Thread
public Handler mHandler;
public void run()
Looper.prepare();
mHandler = new Handler() public void handleMessage(Message msg) // process incoming messages here ;
Looper.loop();
在创建Handler之前,为该线程准备好一个Looper(Looper.prepare),然后让这个Looper跑起来(Looper.loop),抽取Message,这样,Handler才能正常工作。
因此,Handler处理消息总是在创建Handler的线程里运行。而消息处理中,不乏更新UI的操作,不正确的线程直接更新UI将引发异常。因此,需要时刻关心Handler在哪个线程里创建的。
如何更新UI才能不出异常呢?SDK告诉我们,有以下4种方式可以从其他线程访问UI线程:
1.Activity.runOnUiThread(Runnable)
2.View.post(Runnable)
3.View.postDelayed(Runnable,long)
4.Handler
其中,对于View.post(Runnable)方法。在post(Runnable action)方法里,View获取得到当前线程(即UI线程)的Handler,然后将action对象post到Handler里。在Handler里,它将传递过来的action对象包装成一个Message(Message的callback为action),然后将其投入UI线程的消息循环中。在Handler再次处理该Message时,有一条分支(未解释的那条)就是为它所设,直接调用runnable的run方法。而此时,已经路由到UI线程里,因此,我们可以毫无顾虑的更新UI。
因此,注意如下几点:
1.Handler的处理过程运行在创建Handler的线程里
2.一个Looper对应一个MessageQueue
3.一个线程对应一个Looper
4.一个Looper可以对应多个Handler
5.不确定当前线程时,更新UI时尽量调用post方法
android提供的HandlerThread继承自Thread,并且在这个线程里放入了一个Looper(android.os.Looper -> java.lang.Object)对象,也就是说这个Looper对象是在新建线程里运转的。Looper对象可以简单的理解成一个循环处理工具。为了更好的理解HandlerThread,应该先看一下Handler相关的内容。
从Android文档的Handler(android.os.Handler -> java.lang.Object)描述可以看出,Android的Handler和Java的Handler(java.util.logging.Handler -> java.lang.Object)是基本上没有关系的。Android提供的Handler是为了传递消息(android.os.Message)和启动Runnable对象而使用的。
android.os.Handler对象只能在某一个线程中运行并可以使用这个线程的消息队列(Message queue)。从官方API文档可以看到,实例化Handler有4种方法。其中有2种是需要Looper对象参数的,这2种构造方法就可以使Handler运行在包含给定Looper对象的线程中;如果不给定,则运行在当前线程中(一般没有新建线程的情况下,会运行在Android程序主线程中,也就UI线程中)。
android.os.Handler在多线程和IPC(进程间交互)中都起到非常重要的做用。
android.os.Handler可以通过Looper对象实例化,并运行于另外的线程中,Android提供了让Handler运行于其它线程的线程实现,也是就HandlerThread。HandlerThread对象start后可以获得其Looper对象,并且使用这个Looper对象实例Handler,这样Handler就是工作于前面实例的HandlerThread(其实质就是线程Thread)里了。HandlerThread使用常用代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
void useHanlerThread()
// two ways to construct HandlerThread
HandlerThread hThread =
new
HandlerThread(
"AnyName"
,
// a property in android.os.Process
Process.THREAD_PRIORITY_BACKGROUND);
// HandlerThread hThread2 = new HandlerThread("AnyName");
hThread.start();
// get Looper object of hThread(HandlerThread)
Looper looper = hThread.getLooper();
Handler h =
new
Handler(looper)
@Override
public
void handleMessage(Message msg)
// TODO Auto-generated method stub
super.handleMessage(msg);
;
|
HandlerThread是继承于Thread的,所以在Thread的适用范围内HandlerThread是同样适用的。如果在android.os.Handler里需要执行一些非常耗时的任务,就应该把Handler放到HandlerThread里面执行(或者在Handler里新开线程执行),而不应该直接在UI线程中执行,否则会增加出现ANR(Application Not Responding)的可能。
以上是关于Android Handler和HandlerThread的主要内容,如果未能解决你的问题,请参考以下文章