Handler
来源:互联网 发布:手机流水账软件 编辑:程序博客网 时间:2024/06/08 14:24
官方文档
Handler允许你发送和处理与线程消息队列相关的Message和Runnable对象。每个Handler实例都与单个线程及其线程队列相关。当你创建一个新的Handler时,它会绑定到创建它的线程所对应的消息队列,因此,handler会将Message和Runnable分发到消息队列中,当他们被从队列中取出时执行他们。
Handler的两个主要的使用场景:
1. 安排一些在将来执行的Message和Runnable
2. 将一些在不同线程上运行的action推入队列
通过 post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), sendMessageDelayed(Message, long)
方法调度并完成message。当post方法接收到时,会将Runnable对象入队消息队列中。sendMessage方法允许你将绑定数据的Message对象加入消息队列, 然后使用Handler的handleMessage(Message)方法进行处理。(要求你实现一个Handler的子类)
当向Handler使用post或send方法时,你可以选择当消息队列准备好就直接执行,或者指定一个执行前的延迟时间或指定执行的时间。后面的两种允许你实现timeout,ticks和其他的处理时间的接口。
当应用创建一个线程的时候,主线程致力于运行一个处理高水平应用对象(如activities, broadcast receivers等)和创建的窗口的消息队列。你可以创建你自己的线程,然后通过Handler在后台与主线程交互。这些行为一定要在你的新线程调用post或者sendMessage方法之前。Handler中传递的Runnable与Message对象将在Handler的消息队列中被调度,然后再合适的时机被执行。
源码分析
Looper
通过上文我们大概了解了Handler的概念,因为在安卓主线程上不能进行一些耗时的操作,所以我们通常将耗时的操作放到子线程里,并希望子线程执行完操作时通知主线程一声,Handler就很好地解决了这种异步回调的问题。
在主线程中创建Handler上文时候我们直接使用 new Handler() ,但是如果在子线程中这样就会报错, 比如 new Thread (new Runnable (void run () { new Handler();})) ;
这就涉及到一个叫做Looper的东西,Looper被设计用来使一个普通线程变成Looper线程。所谓Looper线程就是循环工作的线程,有点像while(true)的感觉,就是一直监听消息队列里是否有要执行的任务,如果有就拿出来执行,没有就一直在那循环等待监听着消息队列。不是所有的线程都是looper线程(也就是循环等待处理任务的线程啦),安卓默认主线程是Looper线程。Looper线程的创建很简单,使用Looper.prepare();还有Looper.loop()把你自定义的线程包起来就行了(非主线程哦)。下面我们来看看他们的源码:
Looper.prepare():
public static void prepare() { prepare(true);} private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed));}
在线程里在执行prepare方法的时候可能会跑出运行时异常:每个线程只能创建一个Looper,也就是在一个线程里最多只能调用一次Looper.prepare(),当然在主线程就不能调用Looper.prepare()了,因为系统默认给主线程绑定了一个Looper了,这个就不深究源码了,总之在系统在开启主线程的悄悄的调用了Looper.prepare(),总之在主线程就不能调用Looper.prepare()!!
Looper.loop()
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); }}
loop()方法就是让这个循环线程运行起来,每一个Looper都对应唯一一个消息队列,loop()方法里面的那个for(;;)就是循环监听消息队列的,当消息队列内有任务时,就将任务取出执行。
上面说的很凌乱,简单的整理一下,每个线程只能对应一个Looper,每一个Looper对应唯一的一个消息队列,主线程有自己的Looper。
说好的Handler为什么要谈到Looper呢,大家要注意msg.target.dispatchMessage(msg);
这行代码,他在Handler的功能中起到了至关重要的作用,我们一会儿会回过头来详细讲这部分。
Handler
因为子线程中不能进行UI操作,为了解决这个问题,通常情况下我们调用Handler的形式都是如下:
子线程:
Looper.prepare(); //主线程不要加 Message msg = handler.obtain();//新建handler对应的Message对象 .....//将子线程数据封装到message中 handler.sendMessage(msg); //发送message给主线程 Looper.looper(); //主线程不要加 `
主线程:
Handler handler = new Handler() { public void handleMessage(Message msg) { .....//主线程接收到消息,更新UI } }
我们以这两个方法为切入口冲进Handler源码进行分析。
Handler:
//向Handle发送消息 public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0);} //判断是否需要延迟 public final boolean sendMessageDelayed(Message msg, long delayMillis){ if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);} //设定消息执行的时刻 public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis);} //设定将message推入消息队列的时刻 private boolean enqueueMessage(MessageQueue queue, Message msg, longuptimeMillis){ msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis);}
MessageQueue:
//将message推入消息队列 boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true;}
从上面Handler部分的源码可以分析当调用handler.sendMessage(msg)方法时,handler将message添加到当前handler对应的消息队列中(每个handler都唯一对应创建它的线程的Looper,每个Looper都对应唯一一个消息队列,所以可以简单地理解为每个handler都对应唯一的消息队列。但是一个Looper可以对应对个handler, 关系如下图)
每个Looper线程都对应唯一的Looper,每个Looper对应唯一的MessageQueue,但是每个Looper可以对应无数个handler,同理,每个MessageQueue也可以对应无数个handler。简直绕死。。。。
继续上文,handler将message添加到当前handler对应的消息队列时,会判断是否需要延迟(用户如果使用sendMessageDelayed,sendMessageAtTime等延迟操作的),然后设定message入队的时间,将message推入消息队列,也就是调用queue.enqueMessage(msg)方法,这个方法里面的queue就是创建handler的Looper线程中的消息队列(什么是Looper线程上文已阐述,简单来说就是调用了Looper.prepare与Looper.looper的线程!!!当然主线程虽然我们没有手动调用这两个函数但是系统已经帮我们调用了!!!),我们进入enqueMessage函数内部,发现有一个msg.target,/*package*/ Handler target;
, 原来是一个Handler,看起来很有可能是我们使用的handler的引用,我们去Handler中源码中寻找下:
public final Message obtainMessage(){ return Message.obtain(this);}public static Message obtain(Handler h) { Message m = obtain(); m.target = h; return m;}
原来是在调用handler.obtain()时就将调用的handler的引用传入了message,在m.target = h;
处进行赋值。这回可以继续看上面MessageQueue部分的源码了,大概看一下就是判断下当前message应该放入队列的位置,然后通过nativeWake(mPtr);
触发放入操作,剩下就是native层了我们就不去深究,整个发送消息就是酱紫了。
然后呢,他是怎么调用主线程中的handleMessage方法的呢?我们要回到Looper部分最后一句话,Looper就像是一座子线程与主线程沟通的桥梁,在Looper.loop()这个神奇的函数,我们通过这个函数从消息队列中取出消息并执行,我们上文已经注意到msg.target.dispatchMessage(msg);
这行代码,msg.target是当前handler的引用,我们再次回到Handler的dispatchMessage方法中:
Handler:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } /** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. * * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */ public interface Callback { public boolean handleMessage(Message msg); } private static void handleCallback(Message message) { message.callback.run(); }
我们可以猜想通过回调handler中dispatchMessage方法就应该执行我们自定义的消息处理方式了,在dispatchMessage方法中进行了三种处理方式的选择, handleCallback、mCallback.handleMessage和handleMessage,他们代表了三种Handler处理消息的形式,下面我分别介绍下:
if (msg.callback != null) {
handleCallback(msg);
}
我们找到它的实现:
private static void handleCallback(Message message) { message.callback.run();}
寻找到 msg.callback
的定义, msg/*package*/ Runnable callback;
,哈哈哈,原来在封装Message的时候将一个Runnable对象当做msg.callback的引用传入,所以处理message的时候就直接调用Runnable的run方法了。于是开始研究Runnable是怎么传入message的。
Handler:
public final boolean post(Runnable r){ return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
就是他!连Message都不用自己创建,handler直接把Runnabel对象封装到message里面然后加到消息队列里面处理了,如果你想通过这种方式自定义消息处理的方式,就酱紫调用:
handler.post(new Runnable() { void run() { 随便干点啥.. } });
if (mCallback.handleMessage(msg))
{
return;
}
在Handler源码中找到了这个mCallback的定义: final Callback mCallback;
, 还有他的实现,如下:
Handler:
/** * Callback interface you can use when instantiating a Handler to avoid * having to implement your own subclass of Handler. * @param msg A {@link android.os.Message Message} object * @return True if no further handling is desired */ public interface Callback { public boolean handleMessage(Message msg); }
英文的大概意思就是如果你不想写一个内部类来实现对消息结果的处理的话,那你可以直接实现Handler的Callback接口,继续煮个栗子(private public 神马的我都没care, 主要关注函数名):
public class MyClass implements Handler.Callback { ...... Handler handler = new Handler(this); @override void handleMessage(Message msg) { 各种处理啊.... }}
handleMessage(msg);
终于讲到了最后一个,一口老血!这种方式是最常用的,第一个主线程子线程的��就是酱紫,我再烤过来~
子线程:
Looper.prepare(); //主线程不要加Message msg = handler.obtain();//新建handler对应的Message对象.....//将子线程数据封装到message中handler.sendMessage(msg); //发送message给主线程Looper.looper(); //主线程不要加
主线程:
Handler handler = new Handler() { //就是实现他! public void handleMessage(Message msg) { .....//主线程接收到消息,更新UI }}
太好了,我已经被自己无限跳转的思路搞蒙蔽了��,让我做个图来结束这个美好的文档��
Handler处理逻辑图示:
- Handler
- Handler
- Handler
- handler
- handler
- Handler
- Handler
- Handler
- Handler
- Handler
- Handler
- handler
- Handler
- handler
- handler
- Handler
- Handler
- Handler
- NOIP2013模拟最小比例
- 大家好,这是我第一篇在CSDN上的博文,本篇主要讨论小型内存池的设计
- 《剑指offer》-02字符串替换
- ssm利用poi将excel数据导入数据库
- Leetcode 130. Surrounded Regions
- Handler
- Qt---安装
- 关于 LINK : fatal error LNK1561: 必须定义入口点 的解决方法
- Java SE-标识符与命名规则
- 2016下半年工作计划
- 北理工Java实验2.5(文件读入与写出)
- toString()没有参数
- mysql sql语句大全
- 半年总结