• android消息传播机制


    Handler,Looper,Message,MessageQueue学习小结 

    背景介绍

             Andorid消息机制主要是指Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper的支撑。MessageQueue是消息的存储单元,Looper是对这个消息队列进行无限循环查询的操作人。

             关于UI更新,Android规定不能在子线程更新UI,这是ViewRootImpl对UI进行的验证操作。

    void checkThread() {
        if (mThread != Thread.currentThread()) {
                throw new CalledFromWrongThreadException(
                        "Only the original thread that created a view hierarchy can touch its views.");
            }
    }

             每次加载UI的时候ViewRootImpl对更新UI线程进行检查,不是的话则爆一个异常出来。

             因此UI更新的时候需要在主线程。但是在主线程有太多操作会导致ANR,因此复杂操作需要在子线程中进行。

             问题来了,假如需要一个很复杂的操作,其中需要将最后的结果更新在UI中,我们势必不能在主线程中进行了。此时需要一个能在子线程中与主线程进行沟通的桥梁,来协助我们在子线程中,与主线程通信,让主线程来更新UI。因此Android为我们提供了Handler解决这个问题。

             (系统之所以不让我们在子线程中更新UI,是因为UI控件是线程不安全的,若被多个子线程操作更新UI,会导致各种问题。但是我们加锁的话,又会因为粒度太粗导致UI访问的效率降低)

    使用方法

    使用Handler的大致流程:

    1、首先创建一个Handler对象,可以直接使用Handler无参构造函数创建Handler对象,也可以继承Handler类,重写handleMessage方法来创建Handler对象。

    2、Handler创建完毕之后,其内部的Looper以及MessageQueue就可以和Handler一起协同工作。我们可以Post一个Runnabe对象投递到Handler内部的Looper中去处理,也可以通过Handler的send方法发送一个消息,这个消息同样给Looper去处理(Post最终也是使用的send)。3、当Handler的send方法被调用时,他会调用MessageQueue的enqueueMessage方法,将这个消息放入消息队列中,然后Looper一旦发现有新的消息时,就会自动处理这个信息,最终消息中的Runnable或者Handler的handleMessage方法会被调用。(由于Looper是在主线程中的,因此Handler的业务相当于给Looper去操作了,因此就是在主线程中进行的处理)。

    消息机制分析

    -----------------------threadLocal

             ThreadLocal是一个线程内部的数据存储类,我们可以使用他在指定的线程中进行数据的存储。当数据存储之后,只能在制定线程中获取到存储的数据,其他线程则无法获取到数据。

             一般来说,当某些数据是以线程为作用域并且不同线程需要具有不同的数据副本,这个时候就需要使用ThreadLocal。

             ThreadLocal的工作原理,便是在他的set方法

      

    public void set(T value){
            Thread currentThread = Thread.currentThread();//获取当前线程
            Values values = values(currentThread);
            if(values == null){//当前线程下的该值为空则创建一个针对该线程存在的values
                values = initializeValues(currentThread);
            }
            values.put(this,value);
        }

             因此每个值都具有了当前thread的信息,不同thread的话会直接初始化。

             get方法:

    public T get(){
            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);//此时为NULL
            }
            return (T) values.getAfterMiss(this);
        }

             也很明显,也是包含了对线程的判断。因此若是该线程未初始化此参数,便获得的为null,而且不会产生线程不对的情况。

             从ThreadLocal的set和get方法中可以看出,他们所操作的对象都是当前线程的localValues对象的table数组。因此在不同的线程中访问同一个threadLocal的set和get方法,他们对threadLocal所做的读写操作仅限于各自线程的内部,因此可以在多个线程中互不干扰的存储和修改数据。

    消息队列的工作原理

             MessageQueue主要包含两个操作:插入和读取。

             读取操作同时伴随着删除操作。

             插入对应的方法为enqueueMessage

    boolean enqueueMessage(Message msg, long when) {
            if (msg.target == null) {
                throw new IllegalArgumentException("Message must have a target.");
            }
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
            synchronized (this) {
                if (mQuitting) {
                    IllegalStateException e = new IllegalStateException(
                            msg.target + " sending message to a Handler on a dead thread");
                    Log.w(TAG, e.getMessage(), e);
                    msg.recycle();
                    return false;
                }
            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;
        }

             以上为enqueueMessage的函数。此处进行的单链表的插入操作,将消息插入到了队列中。插入完成之后就需要进行读取操作。

             读取操作对应的是next()

       

    Message next() {
        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();
                }
                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 (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);
                }
                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操作的关键部分,可以看出,当msg不为空的时候,不断进行取出,同时将原来的msg = null,因此是读取操作伴随这删除操作进行的。当所有的msg处理完毕之后,就会进入阻塞状态,等待下一个msg的到来。

    Looper的工作原理

             looper在Android的消息机制中扮演着消息循环的角色,他会不断的从MessageQueue中查看是否有新的消息,如果有新的消息,就会立即处理,否则就会一直堵塞。

             Activity创建的时候会自动创建一个looper。我们也可以在别的线程中创建looper,只需要使用looper.prepare()这个方法。

         

    public static void prepare() {
            prepare(true);
        }
    
        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));
        }

             以上为prepare的操作,通过threadlocal来判断该线程是否已经拥有looper,打出来的异常也是一个线程只可以创建一个looper。threadlocal的set方法中同时实例了一个looper。(另有一个方法是prepareMainLooper()用于获取主线程的looper,本质也是prepare)

       

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

             这是looper的构造函数,包含了一个实例的messageQueue和当前的线程信息。

             Looper创建完毕之后,就需要进行工作了。Looper.loop()可以用来开启消息循环。

            

    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;
             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);
                }
                final long traceTag = me.mTraceTag;
                if (traceTag != 0) {
                    Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
                }
                try {
                    msg.target.dispatchMessage(msg);
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                if (logging != null) {
                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                }
                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();
            }
        }

             Loop()的操作从源码中可知也是个死循环,唯一可以退出的msg == null的时候,而msg == null的时候,就是messageQueue的next()方法返回了null,此时是messageQueue执行了quit操作,停止了pose同时返回了null。而loop的quit操作是执行messageQueue的quit操作。因此当loop执行了quit或者messageQueue执行了quit操作时,两者都会同时被关闭。

             除了quit操作之外,looper会无限制的等待messageQueue的内容。

    Handler工作原理

             Handler主要执行信息的发送和接受。发送通过post和send实现。

            

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

             sendMessage执行了sendMessageDelayed,不过延时为0;

            

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

             sendMessageDelayed又执行了sendMessageAtTime方法

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

             sendMessageAtTime方法,实现的是messageQueue的插入操作。

             因此整个send过程只是向messageQueue插入了一条信息。

             Handler将信息给messageQueue,然后由looper进行处理,处理完毕之后需要通知handler来取回去继续处理。looper中进行了

       

    try {
                    msg.target.dispatchMessage(msg);
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }

             msg.target便是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);
            }
        }

             dipatchMessage是handler对消息的处理,handleCallback

      

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

             调用了message的callback方法,这个callback是一个runnable对象,也就是此时执行runnable对象的run方法。

             而msg的runnable对象不存在时,便会执行mCallback的handlemessage方法,mCallback是用于创建一个handler的实例但是不派生handler的子类。方便我们不需要每次都新建一个handler的子类来进行重写handlemessage方法。若mCallback不存在,就直接执行handlemessage方法,也就是我们一开始实例一个handler的时候重写的方法了。

  • 相关阅读:
    常见的排序算法(四):归并排序
    常见的排序算法(三):快速排序
    常见的排序算法(二):选择排序
    a标签没有闭合引起自动插入很多a标签的问题
    js笔记
    Vue笔记
    CSS笔记
    bootstrap--网格化布局
    backgroud图片充满元素的方法
    bootstrap--概述
  • 原文地址:https://www.cnblogs.com/Sample1994/p/6656715.html
Copyright © 2020-2023  润新知