Handler 执行流程及源码解析

2022-05-24  本文已影响0人  蜗牛是不是牛

本文着重于对Handler整体的流程,Looper、MessageQueue、Message、Handler之间如何相互协作运行,sendMessage到handlerMessage之间的代码执行流程。

Looper

UI线程Looper的创建

handler 的创建线程是需要Looper的,UI线程不需要手动创建,在ActivityThread的main方法里被自动创建。

public final class ActivityThread extends ClientTransactionHandler {
    public static void main(String[] args) {
        Looper.prepareMainLooper();
        Looper.loop();
    }
}

通过Looper.prepareMainLooper()创建UI线程Looper对象,内部调用Looper的 prepare() 方法

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        sMainLooper = myLooper();
    }
}

子线程Looper创建

除UI线程外的子线程,需要手动创建Looper,创建方法也是通过Looper.prepare() 和UI线程手动创建Looper的方式一致。

// 创建
Looper.prepare();
// handler 相关代码
// 启动
Looper.loop();

Looper.prepare() 会把创建的Looper对象存入ThreadLocal

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

ThreadLocal 是一个线程内部的存储类,可以把数据存储在指定线程内,存储数据后,只能在指定线程才能获取到存储的数据。

Looper的初始化方法内,会创建MessageQueue对象并作为其成员变量

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

Handler

在Handler 初始化时,会通过ThreadLocal获取当前线程的Looper, 以及Looper内的 MessageQueue;线程内没有Looper会报错;

public Handler(@Nullable Callback callback, boolean async) {
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

在发送消息时,大致有三种方式:

三种方式本质都是同一种实现,差异仅在对Message的处理:

最终都会通过sendMessage方法,调用到MessageQueue的enqueueMessage来实现的。

调用链如下:

public final boolean sendMessage(@NonNull Message msg) {
    return sendMessageDelayed(msg, 0);
}

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

public boolean sendMessageAtTime(@NonNull 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);
}

public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

queue.enqueueMessage(msg, uptimeMillis),就进入到MessageQueue类。

MessageQueue

MessageQueue虽然叫做消息队列,但内部实现是使用单链表的数据结构,来维护消息列表。主要包含两个操作:enqueueMessage(..) 插入数据,next() 读取数据;

enqueueMessage(..)主要实现了单链表的插入:

boolean enqueueMessage(Message msg, long when) {
    ...
    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 {
            // 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;
}

next() 方法源代码过长就不放出了,主要原理是通过死循环,消息队列无消息就阻塞,有消息会返回该消息并从链表中将该消息删除。

MessageQueue中有了新消息,Looper就会立即检测到。

Looper

Looper.loop() 方法内有一个死循环,不停的通过messageQueue.next() 检查messageQueue是否有新消息,

有消息返回并在链表内删除,没有消息则阻塞。

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

Looper检测到新消息是,会调用 msg.target.dispatchMessage(msg)来处理消息。前面我们提到,target就是发送这条消息的Handler对象。

线程在这里被切换

dispatchMessage(msg) 是在Looper内被执行的,这里的Looper正是创建Handler时,线程里环境必要的Looper,这样dispatchMessage(msg) 方法就被切换到Handler创建时的线程了。

这时,由Handler发送的消息又回到了Handler内部,由dispatchMessage(msg)

Handler

public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

这里处理了三种情况:

private static void handleCallback(Message message) {
    message.callback.run();
}

这里执行run()方法,已经达到切换线程的目的了。

public interface Callback {
    boolean handleMessage(@NonNull Message msg);
}

到这里,从sendMessage 到 handlerMessage,中间的过程已经跑通了。

handler 整体流程总结

  1. 在Handler 初始化时,会获取当前线程的looper, 以及 MessageQueue;
  2. handler.SendMessage 方法最终会调用MessageQueue.enqueueMessage(), 将Message插入到MessageQueue链表中,并将Handler 本身赋值给Message 对象的target属性;
  3. MessageQueue在将Message插入到队列后,Looper里的Looper方法会检测到新消息出现,并开始处理消息;
  4. Looper.Loop方法从消息队列里面获取到message后,通过traget属性拿到Handler对象,正是我们enqueueMessage放入的handler 对象;
  5. 调用handler对象的dispatchMessage方法,最终会走到我们继承于Handler的handlerMessage方法,来处理具体的业务;

资料分享

最后,给大家分享一些大佬整理的学习资料,里面包括Java基础、framework解析、架构设计、高级UI开源框架、NDK、音视频开发、kotlin、源码解析、性能优化等资料,还有2022年一线大厂最新面试题集锦,都分享给大家,助大家学习路上披荆斩棘~ 能力得到提升,思维得到开阔~ 有需要的可以在我的【公众号】免费获取!!!

Android framework开发揭秘 2022最新Android中高级面试题汇总
上一篇下一篇

猜你喜欢

热点阅读