Handler消息机制小结

最近复习了下Handler消息机制,跟了一下源码,准备做个小总结作为自身知识归纳,也可以跟博友们一起进行讨论讨论。

一、Handler是什么?它是一种消息机制,是进行线程间通信的。实际上Android的消息机制就是Handler的运行机制。

二、Handler的组成:handler、looper,masseage,messagequene

运行原理:handler通过发消息进入到messagequene中,message携带消息,looper轮询messagequene,将消息发出来,handler接受消息,处理逻辑。

上述运行原理是个非常浅显的,谁都知道的东西。但是深入底层,看底层代码运行原理的时候,我们先需要将每个成员都需要弄懂。

  1. handler:handler的作用就是通过post和send的一系列方法将消息发送到messagequene中。messagequenes收到消息之后,通过looper轮询,将消息处理。具体的细节通过看源码(以sendMessage为例):

 

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
  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;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

这几段代码说明了通过handler.sendMessage走的handler底层的一系列方法,最终将消息放入到了messagequene中,然后looper通过轮询messagequene,然后将消息交给handler,handler处理消息。那这个过程我们看源码:

 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
            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.loop()这个方法就是一直来轮询messagequene的,for(;;)这是个死循环,也就是说handler通过send将消息发送到messagequene中,looper通过轮询messagequene,调用msg.target.dispatchMessage(msg);msg.target看Message源码发现,其实就是Handler对象,也就是调用的就是handler..dispatchMessage()方法。

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

最后调用的就是handleMessage方法,我们在使用handler的时候会重写这个方法,然后处理逻辑。

小结Handler:Handler将消息发送到Messagequene中,Looper通过死循环Messagequene,获取消息,然后将消息交给Handler,Handler通过handleMessage方法处理掉消息。

2. Message:这个其实就是消息的载体,携带消息加入到Messagequene中。其中需要注意的是Message的对象三种创建方式

  • 1.Message msg = new Message(); 
  • 2.Message msg = Message.obtain(); 
  • 3.Message msg = handler.obtainMessage(); 

这三种都是获取消息对象的方式,其中第二种和第三种使用起来较好(其实是一样的),因为obtain源码有个注释说是在Message消息池中返回一个Message实力,这样避免重复创建Message对象,容易造成内存泄漏。其中注意Message几个参数的用法:

  • what:使用者定义这个消息用于区分这个消息到底是属于谁来处理的
  • arg1:arg1和arg2相对于setData方法来讲,当你只是想携带int类型的数据时 我们的使用成本更加低廉
  • arg2:arg1和arg2相对于setData方法来讲,当你只是想携带int类型的数据时 我们的使用成本更加低廉
  • obj:obj可以传递任意类型的数据,但在进程间传递序列化的框架类时必须保证非空,并建议传递其他数据时还是以setData()为主; 设计obj的主要目的是传递引用数据类型,bundle是可以一次性传递多种基本数据类型的载体

 3.Messagequene:消息队列,功能是插入和读取,这两个动作对应的方法也就是Messagequene的两个核心方法

enqueueMessage和next方法,其实Messagequene是通过一个单项列表数据结构进行数据维护的,是一个先进先出的规则,其中enqueueMessage方法就是单向链表的一个插入操作,具体看看next方法:

Message next() {
    // Return here if the message loop has already quit and been disposed.
    // This can happen if the application tries to restart a looper after quit
    // which is not supported.
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }

    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {//死循环
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        //阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的。
        //如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
        //如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
        //如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
               //没有消息,会一直阻塞,直到被唤醒
                nextPollTimeoutMillis = -1;
            }

            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }

            // If first time idle, then get the number of idlers to run.
            // Idle handles only run if the queue is empty or if the first message
            // in the queue (possibly a barrier) is due to be handled in the future.
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }

            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // Run the idle handlers.
        // We only ever reach this code block during the first iteration.
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler

            boolean keep = false;
            try {
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }

            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }

        // Reset the idle handler count to 0 so we do not run them again.
        pendingIdleHandlerCount = 0;

        // While calling an idle handler, a new message could have been delivered
        // so go back and look again for a pending message without waiting.
        nextPollTimeoutMillis = 0;
    }
}

这个next方法通过死循环不断轮询消息,如果有消息就执行,然后删除,没有消息就阻塞。

enqueueMessage这个方法处理了延迟发消息,其实就是messagequene是根据时间长短进行排序的,头时间最小,尾部时间最大,具体原理可查看其他博客有介绍,本章就不介绍了。

4.Looper:在handler里他扮演的一个不断从messagequene中轮询消息的角色,我们都知道Looper.prepare方法返回个looper对象,然后开启Looper.loop开始进行消息循环。首先我们要注意几点:

  • 子线程启用Handler的时候一定要有Looper对象,否则会报错,在Looper的源码里有个loop()的方法,如果Looper对象为空,那么就会抛出个异常,这块可以看看源码我就不贴了,非常简单。
  • 线程和线程进行交互的时候,主要获取当前的Looper就行,主线程默认有个Looper,同时Looper源码中也有获取主线程Looper的方法,具体可以看一下。
  • Looper和线程是绑定的,他俩是一一对应的关系,你想往哪个线程发消息,就持有那个线程的Looper就可以。
  • ThreadLocal这个是存储线程数据的类,它可以在指定的线程中存储数据。,其实存储的的就是当前线程的Looper对象

 

Looper的这个构造方法就是将当前的线程和Looper还有消息队列绑定起来。

在prepare的时候判断threadlocal是否获取到当前Looper对象,如果获取到了,就抛出个异常说明一个线程只能创建一个Looper,如果没获取到就将当前Looper对象存到threadlocal当中。

public void set(T value) {
    Thread currentThread = Thread.currentThread();//得到当前线程
    Values values = values(currentThread);// 得到当前线程的localValues变量的值
    if (values == null) {//首次执行时,会进入这里
        values = initializeValues(currentThread);//新创建一个Values对象
    }
    values.put(this, value);
}

ThreadLocal的set方法存入的其实是个键值对,键就是当前ThreadLocal对象,值就是Looper对象。Values是ThreadLocal的一个静态内部类,localValues是Thread的成员变量,

/**
 * Creates Values instance for this thread and variable type.
 */
Values initializeValues(Thread current) {
    return current.localValues = new Values();
}

初始化的方法其实就是返回个Values对象,其中ThreadLocal的值就是存储到Values内部的一个数组 中,取出的时候:

public T get() {
    // Optimized for the fast path.
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values != null) {
        Object[] table = values.table;
        int index = hash & values.mask;
        if (this.reference == table[index]) {
            return (T) table[index + 1];
        }
    } else {
        values = initializeValues(currentThread);
    }

    return (T) values.getAfterMiss(this);
}

get方法,返回的其实就是当前线程的localValues。其实从set和get方法中也能看出来操作的都是当前的localValues的table【】,所以,当你访问不同的线程对象的时候,其实都是在同一线程内部进行的读写操作。多以多线程的存储和修改数据的时候,是互相没有影响的。

所以说现在Handler消息机制的一些基本内容就复习完了,第一次写博客,可以留言进行相互讨论。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值