1.Handler的作用

Android开发中,我们经常使用Handler进行页面的更新。例如我们需要在一个下载任务完成后,去更新我们的UI效果,因为AndroidUI操作不是线程安全的,也就意味着我们不能在非UI线程中去操作UI,否则会抛出CalledFromWrongThreadException异常。

Handler的基本用法:

Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
//do something
}
}
}; Handler handler1 = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
//do something
}
return false;
}
});
handler.sendEmptyMessage(1);
handler.post(new Runnable() {
@Override
public void run() { }
});
...

上面是我们熟悉的Handler的用法,通过几天的源码阅读,今天写下我对Handler的不完全解析吧。为什么说不完全呢?因为能力有限,所以并没有包括native代码的解析。手动滑稽。


2.源码不完全解析

提前声明下,这里的解析基本上都是建立在网上查阅资料以及自己源码的理解。如果有问题,请指出。

在这里我们先查看Handler的构造方法

//无参构造方法
public Handler() {
this(null, false);
} //参数为Looper的构造方法
public Handler(Looper looper) {
this(looper, null, false);
} //参数为Callback的构造方法
public Handler(Callback callback) {
this(callback, false);
} //参数为Looper和Callback的构造方法
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}

从构造方法看,他们竟然后面的都有一个值为false的参数,我们随便看一个构造方法。

/**
* @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) {
...
mLooper = Looper.myLooper();
if (mLooper == null) {
//在子线程中创建Handler的时候会抛出的异常。
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//把Looper的MessageQueue赋值给Handler的mQuene属性
mCallback = callback;//Callback赋值
mAsynchronous = async;//是否为异步的标志
}

看这个方法的对最后一个参数的注释我们可以知道,最后一个参数是是否异步执行的标志,这个方法又是被隐藏的 @hide ,我们无法显示的调用这个构造方法,但是可以通过反射来调用这个构造方法(待测试)。OK,那么我们可以知道,我们所有的构造方法都是同步的,并没有异步执行。

上面的构造方法中包括了一个Looper对象,一个MessageQueue对象,我们来看看这两个到底是什么?什么时候创建的?

先来看看Looper

public final class Looper {
private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you've called prepare().
//使用ThreadLocal来确保一个线程中只存在一个Looper
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class final MessageQueue mQueue;
final Thread mThread;
...
}

我们接着看下 Looper.myLooper() 这个方法:

public static @Nullable Looper myLooper() {
//直接把sThreadLocal中的Looper对象返回
return sThreadLocal.get();
}

我们可以看到该方法直接返回当前线程的Looper,如果为null的话,那么抛出了一个异常Can't create handler inside thread that has not called Looper.prepare(),当然啦,我们的Handler在主线程中创建的,所以我们得到的Looper对象一定是不为null的。为什么这么肯定呢?这要从App的启动流程说起(有点扯远了,也是我下个阶段正在看的)。我们知道Android 系统基于Linux的,我们每个App都相当于一个是系统的子进行。举个栗子:我们的Eclipse(好久不用了)中有很多个工程,当我们执行某一个工程的时候就相当于Eclipse给我们的工程创建了一个进程,通过调用工程的入口方法——main()方法来启动。我们App启动也是这个流程,入口在ActivityThread中。我们来看下main()方法:

public static void main(String[] args) {
...
//创建mainLooper。这里mark下,一会要看
Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread();
thread.attach(false); if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
} if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
} // End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//开始循环处理消息==>>死循环,应用不会退出
Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited");
}

我们要注意的是 Looper.prepareMainLooper() 这个方法:

public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
} private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//创建一个Looper,并和当前线程“绑定”
sThreadLocal.set(new Looper(quitAllowed));
} public static @Nullable Looper myLooper() {
return sThreadLocal.get();
} //ThreadLocal
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

解释下大概意思:创建一个Looper对象,并与当前线程绑定,接下来看下Looper的构造方法:

private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

构造方法只是创建了一个MessageQueue对象,这里简单看下MessageQueue的构造方法:

//quitAllowed是否允许停止
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();//native方法
}

我们这里看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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
} final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
//调用Handler的dispatchMessage方法
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
} 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();
}
}

从构造方法可以看出,我们的mainLooper是不允许停止的。看到这里我们要转回去看看我们的Handler sendEmptyMessage 是如何处理的:

public final boolean sendEmptyMessage(int what){
return sendEmptyMessageDelayed(what, 0);
} public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();//生成一个Message对象
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
} public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
} 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);//消息入队操作
} private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//这里注意下,把当前Handler对象赋值给target,后面会用到
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

上面的代码也挺简单,执行过程中如果 Messagenull 的话,就生成一个Message,把当前Handler赋值给Message对象的target属性,然后调用MessageQueue 的入队方法enqueueMessage

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(TAG, 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);//唤醒Looper.loop()中的queue.next(),继续执行
}
}
return true;
}

从上面的代码看到,我们的Handler把这条消息放入到MessageQueue的尾部,即先进先出,如果这个队列中没有消息,即当前处于阻塞状态,则唤醒Looper.loop() 中的 queue.next(),继续处理这个消息。我们上面提到了Looper.loop() 这个方法,它是一个死循环,如果当前的消息不为null的时候就去执行msg.target.dispatchMessage(msg),去处理这个消息:

public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
//mCallback的handleMessage方法如果返回true,则不会继续执行handleMessage方法
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
} private static void handleCallback(Message message) {
//这个callback是通过handler.post(new Runnable)方式传递过来的,执行其run()方法
message.callback.run();
} public interface Callback {
public boolean handleMessage(Message msg);
} public void handleMessage(Message msg) {
}

在处理消息的时候,先判断msg.callback是不是为null,如果不为null,则执行其run()方法,否则判断Callback是否为null,如果不为null则执行Callback 中的 handleMessage方法,并根据其返回值来判断是否执行HadlerhandleMessage方法。 所有的方法都是在当前线程去执行的。

By the way, ViewRootImpl中有一个checkThread()方法,用来判断是不是处于 当前 线程

由于能力有限这里我没有对native方法进行解析,有机会的话再去深入研究下。


3.我的理解

简单的说,我认为Looper、Handler就是让当前线程执行当前线程中所有的消息,Handler对象在哪个线程被创建,其消息的执行将会在被创建的那个线程中执行。

基于Handler和Looper的源码,我自己写了一个类似Handler消息机制,便于理解和记忆。源码在GitHub上,欢迎讨论。

Android Handler消息机制不完全解析的更多相关文章

  1. Android Handler消息机制源码解析

    好记性不如烂笔头,今天来分析一下Handler的源码实现 Handler机制是Android系统的基础,是多线程之间切换的基础.下面我们分析一下Handler的源码实现. Handler消息机制有4个 ...

  2. Android Handler 消息机制原理解析

    前言 做过 Android 开发的童鞋都知道,不能在非主线程修改 UI 控件,因为 Android 规定只能在主线程中访问 UI ,如果在子线程中访问 UI ,那么程序就会抛出异常 android.v ...

  3. Android Handler消息机制深入浅出

    尊重原创:http://blog.csdn.net/yuanzeyao/article/details/38408493 作为Android开发者,Handler这个类应该是再熟悉只是了.由于差点儿不 ...

  4. Android全面解析之由浅及深Handler消息机制

    前言 很高兴遇见你~ 欢迎阅读我的文章. 关于Handler的博客可谓是俯拾皆是,而这也是一个老生常谈的话题,可见的他非常基础,也非常重要.但很多的博客,却很少有从入门开始介绍,这在我一开始学习的时候 ...

  5. Android消息机制不完全解析(下)

    接着上一篇文章Android消息机制不完全解析(上),接着看C++部分的实现. 首先,看看在/frameworks/base/core/jni/android_os_MessageQueue.cpp文 ...

  6. Android消息传递之Handler消息机制

    前言: 无论是现在所做的项目还是以前的项目中,都会遇见线程之间通信.组件之间通信,目前统一采用EventBus来做处理,在总结学习EventBus之前,觉得还是需要学习总结一下最初的实现方式,也算是不 ...

  7. Handler消息机制与Binder IPC机制完全解析

    1.Handler消息机制 序列 文章 0 Android消息机制-Handler(framework篇) 1 Android消息机制-Handler(native篇) 2 Android消息机制-H ...

  8. Handler消息机制的一些原理(直接用code讲解)——Android开发

    package com.example.handlertest; import android.os.Bundle; import android.os.Handler; import android ...

  9. 【Android】Handler消息机制

    Handler消息机制主要涉及Looper.Handler.MessageQueue.Message.其中,Looper主要负责获取消息,Handler负责发送消息及处理消息,MessageQueue ...

随机推荐

  1. 关于flash擦除的方法

    一般的Flash,只允许写时将1变成0,而不允许0变成1:仅当擦除时将0变成1.所以写全0xff是没什么意义的 以前对flash只能进行一次写很困惑,这句话解释了原因. norflash就是 对bit ...

  2. CodeForces462B

    Appleman and Card Game Time Limit:1000MS     Memory Limit:262144KB     64bit IO Format:%I64d & % ...

  3. Java豆瓣电影爬虫——减少与数据库交互实现批量插入

    节前一个误操作把mysql中record表和movie表都清空了,显然我是没有做什么mysql备份的.所以,索性我把所有的表数据都清空的,一夜回到解放前…… 项目地址:https://github.c ...

  4. StringUtils工具类常用方法介绍(持续更新)

    StringUtils方法的操作对象是java.lang.String类型的对象,是JDK提供的String类型操作方法的补充,并且是null安全的(即如果输入参数String为null则不会抛出Nu ...

  5. js获取当前时间戳

    当使用js时我们可以使用timestamp = (new Date()).valueOf();直接获取当前时区时间点的时间戳.注意:js中时间戳的单位是毫秒,而php中则是秒

  6. JAVA函数的重载

    一.什么是重载(overlording) 在JAVA中,可以在同一个类中存在多个函数,函数名称相同但参数列表不同.这就是函数的重载(overlording).这是类的多太性表现之一. 二.重载的特征: ...

  7. 做一个360度看车的效果玩玩(web)

    前几天在 Lexus 官网看到有这样的一个效果:http://www.lexus.com.cn/models/es/360 于是顺手打开控制台看了下他们是怎么做的,发现使用的技术还是比较简单的,通过背 ...

  8. xml类型转换列表显示 SQL查询

    数据库中存在字段类型为xml 的数据, 现举例 xml 字段存储的数据为: <MortgageInfoShipList> <ITEMS> <ITEM> <Sh ...

  9. Python简单爬虫记录

    为了避免自己忘了Python的爬虫相关知识和流程,下面简单的记录一下爬虫的基本要求和编程问题!! 简单了解了一下,爬虫的方法很多,我简单的使用了已经做好的库requests来获取网页信息和Beauti ...

  10. wpf的无边框窗体透明

    本人做了3年的winform,来了新的工作,需要使用wpf.对于wpf,本人小白一个.说的不对的地方,请大家多多指导.... 今天先从wpf的窗体属性开始说起! 需要做的项目功能大概和电脑桌面类似,需 ...