【Android】Handler、Looper源码分析
一、前言
源码分析使用的版本是 4.4.2_r1。
Handler和Looper的入门知识以及讲解可以参考我的另外一篇博客:Android Handler机制
简单而言:Handler和Looper是对某一个线程实现消息机制的重要组成部分,另外两个重要元素是Message和MessageQueue,通过这四个类,可以让某个线程具备接收、处理消息的能力。
二、源码剖析
虽然只有四个类,而且这里只是剖析其中两个,但是也不能独立分析,必须组合进行解析。切入点是类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入手,看看这里到底做了什么。
代码一:
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
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的静态方法,即prepare(),前面代码中第5行调用。
第13行可以看到一个运行时异常,其打印信息翻译为:每一个线程只允许拥有一个Looper,而且判断条件中用到ThreadLocal对象,如果不明白这是什么,可以参考我的另外一篇博客:深入理解ThreadLocal。总之,第一次调换用这个方法并且之前没有调用过,则会调用第15行的代码,这里实例化了一个Looper对象,其构造方法如下:
代码二:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
第2行初始化了一个MessageQueue,顾名思义,就是为Looper创建绑定了一个消息队列。
第3行则获取当前线程,即调用Looper的线程。这样即可将Looper绑定到一个线程上,同时为一个线程创建一个消息队列。
在消息机制里面,Looper只是负责管理消息队列,也就是取出消息进行处理,而Handler则是负责发送消息以及处理消息的,那么Handler和Looper又是如何绑定到一起的呢?看切入点里面的7-11行,这里做了什么呢?下面的分析涉及到Looper中的几个方法,这里插入分析一下:
代码三:
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static Looper myLooper() {
return sThreadLocal.get();
} /** Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
很明显可以看到myLooper是获取属于当前线程的Looper,而getMainLooper则是获取应用的主Looper,它由属性sMainLooper引用,其赋值过程如下。
代码四:
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
注释中说到,这个方法不应该由程序员自己调用,我猜测这个方法应该是在应用启动的时候,由属于应用的第一个线程调用,之后如果再次调用,就会抛出异常了,因为sMainLooper实际上是一个static变量,也就是说它是属于整个应用的。
准备完毕,现在回到主题,
代码五:
/**
* Default constructor associates this handler with the {@link Looper} for the
* current thread.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*/
public Handler() {
this(null, false);
}
/**
* Use the {@link Looper} for the current thread with the specified callback interface
* and set whether the handler should be asynchronous.
*
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with represent to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param callback The callback interface in which to handle messages, or null.
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
* @hide
*/
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
} mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
重点在于39-43行。第38行调用myLooper()方法获取属于本线程的Looper,如果你在这之前没有调用Looper.prepare()方法,则会返回null,此时就会抛出异常,要求你在这之前调用Looper.prepare()方法。而平时我们在主线程中使用Handler的时候,并不需要调用Looper.prepare()方法,这是因为主线程默认绑定一个Looper。
接下去43行则是获取Looper的消息队列。
除了这种简单的创建方式之外,Handler也还有别的创建方式,比如:
代码六:
/**
* Use the provided {@link Looper} instead of the default one and take a callback
* interface in which to handle messages. Also set whether the handler
* should be asynchronous.
*
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with represent to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param looper The looper, must not be null.
* @param callback The callback interface in which to handle messages, or null.
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
*
* @hide
*/
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
这里传入了一个Looper,而mLooper的赋值不是获取当前线程的Looper,而是直接取用该looper,这引起一个怀疑:一个Looper(或者说一个线程,因为是线程和Looper是一一对应的关系)可以绑定不止一个Handler,因为很明显我可以用一个Looper通过上述构造方法传入到不同的Handler中去,那么自然而然又想到一个问题:Handler是用于发送和处理消息的,那么当一个Looper绑定多个Handler的时候,发送来的消息肯定都是存储在Looper的消息队列中的,那么处理消息的时候,是怎么处理的呢?每一个Handler都处理一遍么?继续看源码,首先看发送消息的函数:
代码七:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
} public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
} public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
} public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
} public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
} /**
* Enqueue a message into the message queue after all pending messages
* before the absolute time (in milliseconds) <var>uptimeMillis</var>.
* <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
* You will receive it in {@link #handleMessage}, in the thread attached
* to this handler.
*
* @param uptimeMillis The absolute time at which the message should be
* delivered, using the
* {@link android.os.SystemClock#uptimeMillis} time-base.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting. Note that a
* result of true does not mean the message will be processed -- if
* the looper is quit before the delivery time of the message
* occurs then the message will be dropped.
*/
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);
}
为了清晰,前面的方法全部都去掉了注释,只剩下最后一个方法,我们看到,往消息队列中添加消息,最后调用的是方法enqueueMessage。其实现如下:
代码八:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
方法的最后调用了MessageQueue的enqueueMessage方法,从上面的流程可以看到,queue其实就是从mLooper中取出的MessgaeQueue。最终到了这里,消息可以通过Handler顺利压入绑定的Looper中的MessageQueue中去了。接下去就是消息的处理。这里需回到Looper中去,因为循环取出消息进行处理是Looper的工作。
前面切入点代码中可以看到,在调用Looper.prepare()方法,实例化Handler之后,还有一个方法需要调用,即Looper.loop()方法。
代码九:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the 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.recycle();
}
}
前面6-16行就不多解释了,关键看17行,这里是一个死循环,无限循环表示从队列中获取消息;第18行也很关键,这里调用MessageQueue的next方法获取下一个消息,很重要的地方在于注释:might block。可能会阻塞!如果不注意这一点,很可能就会误认为调用该方法,因为当时队列中还没有消息,所以就会执行第21行,直接返回了,而看到这个注释,再加上第20-22行的代码,我们容易猜测,MessageQueue通过在next()方法中返回null来表示整个队列的取消,从而终结消息机制,OK,不多说,言归正传,这一段代码最重要的是看31行:msg.target.dispatchMessage(msg);这行代码预示着如何处理消息!
每一个Message都有一个target属性,该属性的声明如下:
/*package*/ Handler target;
没错,是Handler类型!反观代码,在代码八的第2行,有一行很重要的代码被忽视了:
msg.target = this;
在Handler发送没一个消息进入队列之前,都会将其target设置为自己。从这里就可以看到之前那个问题(红色部分)的答案,消息是交给发送它的Handler处理的!接下来自然要去看的是Handler的dispatchMessage方法:
/**
* 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);
}
}
注释即说明它是处理消息的,在这里可以进行一些回调,这里不说明。主要看第13行,调用了handleMessage()方法,其实现如下:
代码十一:
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
终于到这一步了!注释中就能看到,我们在实例化Handler的子类的时候,是需要重载这个方法的,否则你的消息不会得到处理,实现参见切入点8-11行!具体使用可以参见我的博客Android Handler机制。
三、总结
源码剖析中,主要关注的对象是:Thread,Handler,Looper三个重量级对象是如何绑定到一起的,以及消息是如何在Handler和Looper中存在和传播的,从源码中看这个过程非常清楚。其实整个设计并没有什么新奇的技巧,但是设计非常合理,值得借鉴。
下一篇博客会去探索一下MessageQueue,关于MessageQueue如何管理消息,和Looper一起实现延迟消息,我非常感兴趣。
【Android】Handler、Looper源码分析的更多相关文章
- Android消息机制源码分析
本篇主要介绍Android中的消息机制,即Looper.Handler是如何协同工作的: Looper:主要用来管理当前线程的消息队列,每个线程只能有一个Looper Handler:用来将消息(Me ...
- Android网络框架源码分析一---Volley
转载自 http://www.jianshu.com/p/9e17727f31a1?utm_campaign=maleskine&utm_content=note&utm_medium ...
- OpenGL—Android 开机动画源码分析一
.1 Android开机动画实现方式目前实现Android开机动画的方式主要是逐帧动画和OpenGL动画. ?逐帧动画 逐帧动画是一种常见的动画形式(Frame By Frame),其原理是在“连续的 ...
- Android分包MultiDex源码分析
转载请标明出处:http://blog.csdn.net/shensky711/article/details/52845661 本文出自: [HansChen的博客] 概述 Android开发者应该 ...
- Android -- 消息处理机制源码分析(Looper,Handler,Message)
android的消息处理有三个核心类:Looper,Handler和Message.其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道,因 ...
- Handler Looper源码解析(Android消息传递机制)
Android的Handler类应该是常用到的,多用于线程间的通信,以及子线程发送消息通知UI线程刷新View等等.这里我主要总结下我对整个消息传递机制,包括Handler,Looper,Messag ...
- [Android]简略的Android消息机制源码分析
相关源码 framework/base/core/java/andorid/os/Handler.java framework/base/core/java/andorid/os/Looper.jav ...
- 我的Android进阶之旅------>Android中AsyncTask源码分析
在我的<我的Android进阶之旅------>android异步加载图片显示,并且对图片进行缓存实例>文章中,先后使用了Handler和AsyncTask两种方式实现异步任务机制. ...
- Android 开机动画源码分析
Android系统在启动SystemServer进程时,通过两个阶段来启动系统所有服务,在第一阶段启动本地服务,如SurfaceFlinger,SensorService等,在第二阶段则启动一系列的J ...
随机推荐
- atitit.插件体系设计总结o73.doc
1. 两大类型:微内核(级联树形结构)与巨内核(管理容器,并联结构). 1 2. 通用插件接口 1 3. 插件的绑定and 初始化 2 4. 微内核插件平台设计 2 5. 参考 2 1. 两大类型:微 ...
- Essential C++中文版——满汉全席之外
满汉全席之外 Stanley B. Lippman 所著的C++ Primer 雄踞书坛历久不衰,堪称C++最佳教科书.但是走过十个年头之后,继1237 页的C++ Primer 第3 版,Lippm ...
- 【转】Oracle 执行动态语句
1.静态SQLSQL与动态SQL Oracle编译PL/SQL程序块分为两个种:其一为前期联编(early binding),即SQL语句在程序编译期间就已经确定,大多数的编译情况属于这种类型:另外一 ...
- Android PullToRefresh (ListView GridView 下拉刷新) 使用详解 (转载)
最近项目用到下拉刷新,上来加载更多,这里对PullToRefresh这控件进行了解和使用. 以下内容转载自:http://blog.csdn.net/lmj623565791/article/deta ...
- 移动平台自动化测试从零开始-MonkeyRunner工具使用 (第二节)
选自:http://yuedu.baidu.com/search?word=%D3%DA%D3%BF&pbook=0 第2节 MonkeyRunner 安装部署 如果您已经正确的安装部署了A ...
- 【地图API】地址录入时如何获得准确的经纬度?淘宝收货地址详解
用户需求:管理者需要录入一批商户,并在地图上把商户展示出来.但发现一些商户的地址描述并不清楚,导致商户位置出错.如何获得更加准确的商户位置呢? 分析:假设地址准确的,可以通过地址解析,得到准确的经纬度 ...
- [MS bug]安装SQL Server 2008 错误:is not a valid login or you do not have permission
环境: Windows 7 sp1 x64. 问题描述: 安装到几乎要完成的时候爆出:is not a valid login or you do not have permission.安装失败. ...
- Java httpclient请求,解决乱码问题
public class HttpPostRequestUtil { public HttpPostRequestUtil() { } public static String post(String ...
- Codeforces Beta Round #80 (Div. 2 Only)【ABCD】
Codeforces Beta Round #80 (Div. 2 Only) A Blackjack1 题意 一共52张扑克,A代表1或者11,2-10表示自己的数字,其他都表示10 现在你已经有一 ...
- ASP.NET弹出显示ex.Message异常信息 存在换行符和回车符处理办法。
1.把ex.Message换成任意字符串,检验在catch语句块中可以用Response.Write方法显示对话框.结果显示成功,说明问题就出在ex.Message上. 2.在程序中下断点,可以看到e ...