android handler作用,Android Handler之原理解析

  • Post author:
  • Post category:其他


简单回顾一下Handler机制中几个对象的主要作用

Handler机制中最重要的四个对象

Handler:负责发送消息及处理消息

Looper:复制不断的从消息队列中取出消息,并且给发送本条消息的Handler

MessageQueue:负责存储消息

Message:消息本身,负责携带数据

那么,一个消息从发送出去,到回到Handler自己身上,这个过程具体是怎样的?

这个就不得不去看源码了

我们从Android Handler之从主线程往子线程发消息(一)中的

四、怎么从主线程发送消息到子线程?(虽然这种应用场景很少)的示例代码看起

Thread thread = new Thread(){

@Override

public void run() {

super.run();

//初始化Looper,一定要写在Handler初始化之前

Looper.prepare();

//在子线程内部初始化handler即可,发送消息的代码可在主线程任意地方发送

handler=new Handler(){

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

//所有的事情处理完成后要退出looper,即终止Looper循环

//这两个方法都可以,有关这两个方法的区别自行寻找答案

handler.getLooper().quit();

handler.getLooper().quitSafely();

}

};

//启动Looper循环,否则Handler无法收到消息

Looper.loop();

}

};

thread.start();

//在主线程中发送消息

handler.sendMessage();

选择这个蹩脚的demo是为了让大家更好的理解Handler机制,耐心看下去,最后你心中所有的疑问都会有答案

一、先来解释第一行代码

Looper.prepare();

这个很好解释,只要查看Handler的构造方法即可

//空参的构造方法,这个方法调用了两个参数的构造方法

public Handler() {

this(null, false);

}

//两个参数的构造方法

public Handler(Callback callback, boolean async) {

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;

}

看到了没有,Handler的构造方法中会验证Looper,如果Looper为空,那么会抛出空指针异常

如果你足够细心,你会发现,Handler在构造方法中还做了一件事,

将自己的一个全局消息队列对象(mQueue)指向了Looper中的消息队列

即构造方法中的这行代码

mQueue = mLooper.mQueue;

先记住,有用

二、第二行代码是初始化了Hanlder并且重写HandleMessage()方法

这个没什么好解释的

三、我们调用了Looper.loop()方法,这个方法的具体原理随后解释

四、handler.sendMessage(message)的主要作用

最接近我们使用的就是handler.sendMessage(message);这行代码了,那么我们从这行代码看起,看看这行代码之后发生了什么,为了方便大家看到,我把代码执行流程画了出来

f7cabfe19720

sendMessage()之后代码执行流程

红线画出来的代表代码是哪个类的,可以看到,我们sendMessage()之后代码通过图中所示的几个方法,最终执行到了MessageQueue的enqueueMessage()方法。也就是说,接下来我们要看的就是MessageQueue中的方法了。

上源码

boolean enqueueMessage(Message msg, long when) {

if (msg.target == null) {

throw new IllegalArgumentException(“Message must have a target.”);

}

synchronized (this) {

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 {

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;

}

为了方便阅读,我删减了部分代码

可以看到,MessageQueue是一个单向列表结构,而MessageQueue的enqueueMessage()方法主要做的事情就是将Handler发送过来的Message插入到列表中。

也就是说,当我们调用handler.senMessage()方法的时候,最终的结果只是将这个消息插入到了消息队列中

上面的流程图中有一个问题:

最后一行代码queue.enqueueMessage()中的queue对象是什么时候初始化的?

还记得本篇博客在解释Looper.prepare()方法部分的最后一段话吗?

不记得了就往上翻一翻

发送消息的工作已经完成,那么Looper是什么时候取的消息,取出来消息又是怎么送回给Handler的呢?现在我们就不得不看Looper.loop()方法了

Looper.loop()方法

public static void loop() {

for (;;) {

Message msg = queue.next(); // might block

if (msg == null) {

// No message indicates that the message queue is quitting.

return;

}

try {

msg.target.dispatchMessage(msg);

}

}

}

同样的,为了降低阅读难度,我删掉了大部分的代码,只留下了这么几行核心代码,现在我们来看这几行代码的逻辑

这是一个死循环

这个循环的目的是从MessageQueue中取出消息

取消息的方法是MessageQueue.next()方法

取出消息后调用message.target对象的dispatchMessage()方法分发消息

循环跳出的条件是MessageQueue.next()方法返回了null

不过,看到这里我们应该自然会想到,message.target.是哪个对象?

该对象的dispatchMessage()方法都做了什么操作?

带着疑问,我们进入Message类中去寻找target

public final class Message implements Parcelable {

/*package*/ int flags;

/*package*/ long when;

/*package*/ Bundle data;

/*package*/ Handler target;

/*package*/ Runnable callback;

// sometimes we store linked lists of these things

/*package*/ Message next;

}

看到了吧,Message类中有一个成员变量 target,而这个target是一个Handler,也就是说,在Looper从MessageQueue中取出Message之后,调用了Handler的dispatchMessage()方法。

这里我们不禁要问,这个target指向了哪个Handler

带着这个疑问,我翻遍了Message类,也没有看到我想要的答案。此时我就想,既然Handler发送了消息就能接收到消息,那么会不会是在发送消息的途中发生了什么细节是我不知道的,那么我只好仔细看发送消息过程中的代码,终于让我发现了这个

//Handler的方法

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

msg.target = this;

if (mAsynchronous) {

msg.setAsynchronous(true);

}

return queue.enqueueMessage(msg, uptimeMillis);

}

在这个方法的第一行代码就是,将message的target属性赋值为发送message的handler自身。如果读者忘记了这个方法的调用时机,请往上翻翻,查看一下第四部分sendMeeage()中的流程图

也就是说,Looper取出消息后,调用了发送消息的Handler的dispatchMessage()方法,并且将message本身作为参数传了回去。到此时,代码的执行逻辑又回到了Handler中。

接着看handler的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);

}

}

看到这里面一个我们非常熟悉到方法了没有?—handleMessage()方法,对,这个方法就是我们经常要重写的handleMessage()方法,也是我们处理消息时候的逻辑。

到这里,Handler机制工作的主要流程就完成了。

再来一个系统的工作示意图

f7cabfe19720

Handler工作示意图

如果读者认真看了博客,那么到这里就应该对Handler的基本工作流程比较清晰了。我也在前面详细讲解了流程图中的方法调用过程。不过还有一些问题我们没有处理

一个线程中最多可以有几个Looper和几个MessageQueue?

我们来看Looper的构造方法即初始化方法

//Looper暴露出的静态初始化方法

//这个方法会调用下面的私有静态方法

public static void prepare() {

prepare(true);

}

//Looper私有的静态方法

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));

}

//私有的构造方法,禁止外界调用

private Looper(boolean quitAllowed) {

mQueue = new MessageQueue(quitAllowed);

mThread = Thread.currentThread();

}

//从sThreadLocal中获取一个Looper

public static @Nullable Looper myLooper() {

return sThreadLocal.get();

}

解释一下上面三个方法

我们只能通过Looper.prepare()方法去初始化一个Looper

Looper.prepare(boolean)方法的逻辑是一个线程中只能有一个Looper对象,否则在第二次尝试初始化Looper的时候,就会抛出异常。

以线程为单位存储Looper的主要逻辑是通过ThreadLocal实现的

私有的构造方法,禁止外界任意new出一个Looper

通过这段逻辑我们可以看出,

一个线程中最多有一个Looper

接着看Looper的构造方法,里面有一行代码

mQueue = new MessageQueue(quitAllowed);

是的,我们的MessageQueue是随着Looper的初始化而初始化的。那么,MessageQueue能不能随意的被new出来呢?

//MessageQueue的构造方法

MessageQueue(boolean quitAllowed) {

mQuitAllowed = quitAllowed;

mPtr = nativeInit();

}

可以看到,MessageQuede的构造方法是default的,也就是说,只有跟MessageQueue同一个包下才可以实例化MessageQueue,换句话说,我们用户是无法直接new一个MessageQueue对象出来的。而因为Looper在一个线程中只能有一个,从而导致MessageQueue也只能有一个。