美文网首页
Handler 执行流程及源码解析

Handler 执行流程及源码解析

作者: 蜗牛是不是牛 | 来源:发表于2022-05-24 15:41 被阅读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;
    }
    
    

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

    • handler.sendMessage;
    • handler.obtainMessage;
    • handler.post(runable);

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

    • sendMessage 需要我们new Message对象
    • obtainMessage 会获得Message的单例对象
    • post(runable) 先通过obtainMessage获得Message对象,然后设置Message的Callback属性

    最终都会通过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);
        }
    }
    
    

    这里处理了三种情况:

    • 方法内的callback正是我们前文提到的Handler.post(Runnable)方式下设置的callback
    private static void handleCallback(Message message) {
        message.callback.run();
    }
    
    

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

    • 方法内的 mCallback 是不使用Handler派生类来创建的Handler,继承自Callback接口
    public interface Callback {
        boolean handleMessage(@NonNull Message msg);
    }
    
    
    • 方法内的handlerMessage(msg)正是我们继承Handler重写的 handlerMessage()方法。

    到这里,从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中高级面试题汇总

    相关文章

      网友评论

          本文标题:Handler 执行流程及源码解析

          本文链接:https://www.haomeiwen.com/subject/viijprtx.html