Handler机制源码分析

栏目: 后端 · 发布时间: 5年前

内容简介:Handler 是Android SDK中用来处理异步消息的核心类,子线程可以通过handler来通知主线程进行ui更新。备注:本文源码截图 基于Android sdk 28Handler机制 消息发送主要流程如图

Handler 是Android SDK中用来处理异步消息的核心类,子线程可以通过handler来通知主线程进行ui更新。

备注:本文源码截图 基于Android sdk 28

Handler机制 消息发送主要流程如图

Handler机制源码分析

一、创建消息

推荐使用Message.obtain()方法 获取一个新消息,优势在于 Message 内部使用链表维护的一个大小为50的消息池,消息池会回收之前使用过的消息, 提高性能。

/**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    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();
    }
复制代码
/**
     * Recycles a Message that may be in-use.
     * Used internally by the MessageQueue and Looper when disposing of queued Messages.
     */
    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        .....//省略初始化相关代码
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
复制代码

Message使用what属性来区分消息类型, 使用如下另外字段来传递简单数据, 当然也可以使用Bundle来传递任意类型数据

public int what;
    public int arg1;
    public int arg2;
    public Object obj;
    /**
     * Sets a Bundle of arbitrary data values. Use arg1 and arg2 members
     * as a lower cost way to send a few simple integer values, if you can.
     * @see #getData()
     * @see #peekData()
     */
    public void setData(Bundle data) {
        this.data = data;
    }
复制代码

二、Handler发送消息

使用Handler发送消息有两类方法,使用post系列方法, 或 sendMessage系列方法, 但最终发送消息的逻辑在sendMessageAtTime()方法中。

public final boolean post(Runnable r){
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
复制代码

参数uptimeMillis 表示消息 被处理的时间点,是个绝对时间。

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;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
复制代码

三、待处理消息的消息队列

Handler发送消息后, 将MessageQueue将待处理消息添加到消息队列中。该消息队列使用单向链表来实现,根据消息执行的绝对时间 来对单向链表排序,when值越小,即最先被执行的消息,放在表头。

boolean enqueueMessage(Message msg, long when) {
        ····
        synchronized (this) {
            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;
    }
复制代码

四,Loop循环

应用程序启动后,首先执行 ActivityThread 中的main函数, 开始Looper.loop()循环,

public static void main(String[] args) {
        ......
        Looper.prepareMainLooper();
        ......
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
复制代码

loop()循环 是一个死循环, 不停地从MessageQueue.next()方法 中取出消息处理, MessageQueue.next()是个阻塞的方法, 当没有所有消息都取出后,该方法会阻塞线程。

public static void loop() {
        final Looper me = myLooper();
        final MessageQueue queue = me.mQueue;

        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);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
   
            msg.recycleUnchecked();
            ......
    }
复制代码

msg.target 即为发送消息的Handler,最后消息会传到该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);
        }
    }
复制代码

#####五,ThreadLocal 在子线程中使用创建 Handler时,运行时会有如下错误,原因在于当前线程 没有绑定Looper对象

"No Looper; Looper.prepare() wasn't called on this thread."
复制代码

在主线程启动,在main函数中首先会调用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));
    }
复制代码

从Thread源码中,可以看到,每个线程有一个 ThreadLocal.ThreadLocalMap,并且该map由ThreadLocal维护

/* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
复制代码

ThreadLocal 使用自身为key,向该map中保存数据。

/**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

    /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    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中,维护一个static类型的ThreadLocal 变量, 并通过调用prepare() 方法,将looper()对象绑定到当前线程中,

// sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
复制代码

完~ (如果错误或不足,望指出, 大家共同进步)


以上所述就是小编给大家介绍的《Handler机制源码分析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

鸟哥的Linux私房菜

鸟哥的Linux私房菜

鸟哥 / 人民邮电出版社 / 2010-6-28 / 88.00元

本书是最具知名度的Linux入门书《鸟哥的Linux私房菜基础学习篇》的最新版,全面而详细地介绍了Linux操作系统。全书分为5个部分:第一部分着重说明Linux的起源及功能,如何规划和安装Linux主机;第二部分介绍Linux的文件系统、文件、目录与磁盘的管理;第三部分介绍文字模式接口 shell和管理系统的好帮手shell脚本,另外还介绍了文字编辑器vi和vim的使用方法;第四部分介绍了对于系......一起来看看 《鸟哥的Linux私房菜》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

SHA 加密
SHA 加密

SHA 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试