从源码角度深入理解Handler处理机制
2016-01-13 00:00
267 查看
摘要: 深入了解android Handler处理机制,梳理Handler、Looper、MessageQueen、Message之间的关系
一开始接触android的同学应该都知道,一般情况下android是不能在子线程中更新ui的。要更新ui界面的时候我们就需要用到android给我们提供的Handler机制。
Handler机制中核心的几个类有Handler、Looper、MessageQueen、Message这四个类,下面我们来一一剖析。
首先来谈谈Handler的用法(HOW):
Handler中发送消息的函数可以分为两类:一类是post系列,一类则是sendMessage系列。
1)post系列中包括如下方法:
post(Runnable)
postAtTime(Runnable, long)
postDelayed(Runnable, long)
方法的具体意义这里不做解释,不知道的朋友可以去查下文档。post方法是容许你排列一个Runnable的线程对象到主线程队列中,具体的实现我们待会儿再看。
2)sendMessage系列中包括的方法也大致和post中的差不多
sendEmptyMessage(int)
sendMessage(Message);
sendMessageAtTime(Message,long);
sendMessageDelayed(Message, long);
Handler类的部分源码:
如上是Handler中的post(Runnable)的方法,我们可以看到实际上是调用的Handler中的sendMessagexx系列的方法,所以这两种实际上是相同的。然后下面我们来看看sendMessageAtTime方法:
请大家看最后一行,我们可以看到sendMessageAtTime方法最后的结果enqueueMessage,enqueueMessage我想不用说看字面意思都应该很好理解就是一个将msg放入消息队列,也就是MessageQueue中,还有大家看到上面的一个判断queue==null,如果queue为null的画则会抛出错误,这也就是说我们在sendMessage方法之前必须先新建一个messageQueue,在Android中UI线程在启动时就给我们建立了一个MessageQueue,大家不信可以去看看相关源码,这里不多做解释,然后如果我们要在自己的子线程中用到拥有自己的Handler处理,则需要在子线程中维持一个该线程的MessageQueue,那么这个MessageQueue是怎样产生的呢,下面来看到Looper类,对就是Looper类:
就是在这里,我们在子线程中自己管理Looper对象的时候往往都会这样使用
上面是Looper类中的注释,介绍对Looper的使用,注意顺序 Looper.prepare(); XXX; Looper.loop()这样的顺序,仔细看前面prepare的函数通过调用此函数我们可以得到一个MessageQueue的对象,也就是说MessageQueue的对象是在这里产生的,所以Android给我们的使用示例会先调用prepare然后再是loop,还有一点需要注意一个子线程最多只能有一个MessageQueue对象,如果你调用两次prepare则会产生Only one Looper may be created per thread的异常,我相信很多人应该都遇到过这个异常吧,反正本人初学Android之时是遇到过,后来解决了,只是不知道为什么会这样,这就是原因。
loop函数则是一个死循环不断的去取MessageQueue中的Message来,然后去执行里边的逻辑。那么还有一个很重要的问题没有解决,那就是当looper取到MessageQueue里边的Message之后,我们都知道这时候应该要执行我们的Handler类中的
此方法了,最后由我们客户端去实现handleMessage的具体逻辑,对吧?那么问题是怎么来的,还有Looper取到的Message怎么知道应该执行哪一个handler的handleMessage方法呢?要知道我们可能在程序中声明很多个handler,那么我们接着往下看Message类
在Message类中的103行声明有一个target变量,对就是这个target!这个target变量存储的就是对这个消息是属于哪一个Handler的一个引用,也就是说这个消息需要哪个Handler来处理。就是它!这样就全部联系起来了是吧。
我们再次回过头去看Looper类中的Loop方法,这样大家就很容易理解了。
大家仔细看看上面的代码for(;;)里边的内容就是我上面所说的不断循环去读取Messagequeue中的Message当读取到其中一个Message后执行了这行代码:
由上面的讲解我们知道msg.target是一个Handler对象这样我们就回到了我们Handler类的dispatchMessage方法,有人会问不对呀我们最后都是在HandlerMessage方法中处理的呀,先别捉急,心急可是吃不了热豆腐哦,我们来看这个dispatchMessage方法到底是何方神圣,好回到Handler类了:
这个函数很简单,大家看到没有这里有两种处理方法,第一种当msg.callback不为空则执行handleCallback,第二种当msg.callback为空的事后则执行了我们所熟悉的handlerMessage方法,我想第二种方式大家应该都知道,handleMessage大家应该都重写过这个方法。那么来说说第一种,回到最开始,我们最初的起点就是这里,绕了一圈又回来来的!我们最开始的时候说了,Handler发送消息分为两个系列,虽然这两个系统在发送消息时都会调用同一个函数,但是在回调处理的时候还是略有不同。第一种就是post(Runnable),我们都知道Runnable是一个线程,那么我们可以猜想handleCallback应该是执行我们线程里边的run方法,这样才合理嘛,回调回来。那我们下面就看看是不是这样的
看到没有我们的大Android系统果然没有让我们失望,就一句好执行回调的run方法。
最后回头来看我们handler发送消息的两种方式,分别对应上面说的两种回调方式,一目了然有木有!
一开始接触android的同学应该都知道,一般情况下android是不能在子线程中更新ui的。要更新ui界面的时候我们就需要用到android给我们提供的Handler机制。
Handler机制中核心的几个类有Handler、Looper、MessageQueen、Message这四个类,下面我们来一一剖析。
首先来谈谈Handler的用法(HOW):
Handler中发送消息的函数可以分为两类:一类是post系列,一类则是sendMessage系列。
1)post系列中包括如下方法:
post(Runnable)
postAtTime(Runnable, long)
postDelayed(Runnable, long)
方法的具体意义这里不做解释,不知道的朋友可以去查下文档。post方法是容许你排列一个Runnable的线程对象到主线程队列中,具体的实现我们待会儿再看。
2)sendMessage系列中包括的方法也大致和post中的差不多
sendEmptyMessage(int)
sendMessage(Message);
sendMessageAtTime(Message,long);
sendMessageDelayed(Message, long);
Handler类的部分源码:
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } public final boolean postAtTime(Runnable r, long uptimeMillis) { return sendMessageAtTime(getPostMessage(r), uptimeMillis); }
如上是Handler中的post(Runnable)的方法,我们可以看到实际上是调用的Handler中的sendMessagexx系列的方法,所以这两种实际上是相同的。然后下面我们来看看sendMessageAtTime方法:
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); }
请大家看最后一行,我们可以看到sendMessageAtTime方法最后的结果enqueueMessage,enqueueMessage我想不用说看字面意思都应该很好理解就是一个将msg放入消息队列,也就是MessageQueue中,还有大家看到上面的一个判断queue==null,如果queue为null的画则会抛出错误,这也就是说我们在sendMessage方法之前必须先新建一个messageQueue,在Android中UI线程在启动时就给我们建立了一个MessageQueue,大家不信可以去看看相关源码,这里不多做解释,然后如果我们要在自己的子线程中用到拥有自己的Handler处理,则需要在子线程中维持一个该线程的MessageQueue,那么这个MessageQueue是怎样产生的呢,下面来看到Looper类,对就是Looper类:
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } 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)); }
就是在这里,我们在子线程中自己管理Looper对象的时候往往都会这样使用
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(); * } * }
上面是Looper类中的注释,介绍对Looper的使用,注意顺序 Looper.prepare(); XXX; Looper.loop()这样的顺序,仔细看前面prepare的函数通过调用此函数我们可以得到一个MessageQueue的对象,也就是说MessageQueue的对象是在这里产生的,所以Android给我们的使用示例会先调用prepare然后再是loop,还有一点需要注意一个子线程最多只能有一个MessageQueue对象,如果你调用两次prepare则会产生Only one Looper may be created per thread的异常,我相信很多人应该都遇到过这个异常吧,反正本人初学Android之时是遇到过,后来解决了,只是不知道为什么会这样,这就是原因。
if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); }
loop函数则是一个死循环不断的去取MessageQueue中的Message来,然后去执行里边的逻辑。那么还有一个很重要的问题没有解决,那就是当looper取到MessageQueue里边的Message之后,我们都知道这时候应该要执行我们的Handler类中的
handleMessage(Message msg) { }
此方法了,最后由我们客户端去实现handleMessage的具体逻辑,对吧?那么问题是怎么来的,还有Looper取到的Message怎么知道应该执行哪一个handler的handleMessage方法呢?要知道我们可能在程序中声明很多个handler,那么我们接着往下看Message类
103 /*package*/ Handler target;
在Message类中的103行声明有一个target变量,对就是这个target!这个target变量存储的就是对这个消息是属于哪一个Handler的一个引用,也就是说这个消息需要哪个Handler来处理。就是它!这样就全部联系起来了是吧。
我们再次回过头去看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(); } }
大家仔细看看上面的代码for(;;)里边的内容就是我上面所说的不断循环去读取Messagequeue中的Message当读取到其中一个Message后执行了这行代码:
msg.target.dispatchMessage(msg);
由上面的讲解我们知道msg.target是一个Handler对象这样我们就回到了我们Handler类的dispatchMessage方法,有人会问不对呀我们最后都是在HandlerMessage方法中处理的呀,先别捉急,心急可是吃不了热豆腐哦,我们来看这个dispatchMessage方法到底是何方神圣,好回到Handler类了:
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
这个函数很简单,大家看到没有这里有两种处理方法,第一种当msg.callback不为空则执行handleCallback,第二种当msg.callback为空的事后则执行了我们所熟悉的handlerMessage方法,我想第二种方式大家应该都知道,handleMessage大家应该都重写过这个方法。那么来说说第一种,回到最开始,我们最初的起点就是这里,绕了一圈又回来来的!我们最开始的时候说了,Handler发送消息分为两个系列,虽然这两个系统在发送消息时都会调用同一个函数,但是在回调处理的时候还是略有不同。第一种就是post(Runnable),我们都知道Runnable是一个线程,那么我们可以猜想handleCallback应该是执行我们线程里边的run方法,这样才合理嘛,回调回来。那我们下面就看看是不是这样的
private static void handleCallback(Message message) { message.callback.run(); }
看到没有我们的大Android系统果然没有让我们失望,就一句好执行回调的run方法。
最后回头来看我们handler发送消息的两种方式,分别对应上面说的两种回调方式,一目了然有木有!
handler.post(new Runnable() { @Override public void run() { //anything what you can do } })
mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; mHandler.sendMessage(msg)
相关文章推荐
- Android开发笔记之:Handler Runnable与Thread的区别详解
- android的消息处理机制(图文+源码分析)―Looper/Handler/Message
- Android消息处理机制Looper和Handler详解
- AsyncTask陷阱之:Handler,Looper与MessageQueue的详解
- Android编程开发之seekBar采用handler消息处理操作的方法
- Android中的Handler与多线程应用实例
- android开发教程之handler异步更新ui
- Android定时器和Handler用法实例分析
- Toast和Handler的间隔使用实例
- Android中AsyncTask与handler用法实例分析
- android开发教程之android的handler使用方法
- 详解Android中Handler的使用方法
- 详解Android中Handler的内部实现原理
- Android通过Handler与AsyncTask两种方式动态更新ListView(附源码)
- Android开发笔记 Handler使用总结
- PHP中set error handler函数用法小结
- Android中Handler引起的内存泄露问题解决办法
- Android编程中Handler原理及用法实例分析
- Android Looper简介