Handler

作者: 米豆同学 | 来源:发表于2019-04-28 23:18 被阅读0次

Handler

1. ThreadLocal

线程本地存储为使用相同变量的每个不同线程都创建不同的存储,每个线程虽然使用同一个变量,但是变量状态互不干扰。

  • 原理:
    每个线程有一个ThreadLocalMap 对象用来存储该线程ThreadLocal类型的数据
class Thread implements Runnable {
  ...
      /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
}

ThreadLocalMap 是自定义HashMap,以ThreadLocal 对象为key,要使用的数据为value

当调用ThreadLocal的get/set方法的时候首先获得该线程的threadLocals变量,然后根据当前的TreadLocal对象为key,在该线程中查找value。

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

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

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

对于一个线程来说,不同的ThreadLoacal对象都存在这个ThreadLocalMap 对象中,以ThreadLoacal对象为key查找对应的值。

对于一个ThreadLoacal对象来说,数据的拷贝存储在不同线程的ThreadLocalMap 对象中,每次操作(set/get)先找出对应线程的ThreadLocalMap 对象。由此实现多个线程中互不干扰的存储和修改数据

Handler实现原理

1. 消息Message和消息队列MessageQueue

(1)Message

Message使用享元设计模式。定义:对象共享,避免创建多个对象

Message 不能直接new创建,需要调用其obtain方法

    Message message = Message.obtain();
    message.what = 100;
    mHandler.sendMessage(message);

Message.obtain 每次都是从Message对象池中选一个对象,而不是创建新的对象,当一个消息处理完了之后,在Looper.loop 中调用完msg.target.dispachMessage(msg)之后会调用recycleUnchecked,将Message对象放到对象池中。
Message对象池中的对象以链表形式存储,每次obtain取出一个对象,sPoolSize--,每次recycleUnchecked 插入一个对象sPoolSize++,sPoolSize最大为50.

Message.java

   /**
     * 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.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

(1)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 {
                // 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.
                //p.target == null 是“同步分割栏”,如果是同步分割栏,也需要调整唤醒时间
                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;
    }

needWake 表示最早的唤醒时间变了,需要唤醒队列。

同步分割栏: 所谓“同步分割栏”,可以被理解为一个特殊Message,它的target域为null。它不能通过sendMessageAtTime()等函数打入到消息队列里,而只能通过调用Looper的postSyncBarrier()来打入。

“同步分割栏”就像一个卡子,卡在消息链表中的某个位置,当消息循环不断从消息链表中摘取消息并进行处理时,一旦遇到这种“同步分割栏”,那么即使在分割栏之后还有若干已经到时的普通Message,也不会摘取这些消息了。请注意,此时只是不会摘取“普通Message”了,如果队列中还设置有“异步Message”,那么还是会摘取已到时的“异步Message”的,然后在执行普通Message。

frameworks/base/core/java/android/os/Message.java

public void setAsynchronous(boolean async) {
    if (async) {
        flags |= FLAG_ASYNCHRONOUS;
    } else {
        flags &= ~FLAG_ASYNCHRONOUS;
    }
}

源码中 Choreographer中多次用到,为了保证绘制消息不被阻塞优先执行,防止绘制卡顿。

frameworks/base/core/java/android/view/Choreographer.java

 Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
 msg.setAsynchronous(true);
 mHandler.sendMessageAtFrontOfQueue(msg);
  • next 循环计算下一个消息的时间,当消息队列中没有消息或者下一个消息需要等待一段时间时,不会一直跑for循环,而是进入睡眠状态,当有消息到来时,消息队列被唤醒,查找新的消息并返回。
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();
                            }
            //没有消息或者消息时间没到。则进入睡眠状态,使用epoll_wait
            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 {
                    // 没有消息 -1 是一直等待
                    nextPollTimeoutMillis = -1;
                }

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


            ... ...

        }
    }

2. Looper

Looper 采用线程本地存储方式ThreadLocal,一个线程只有一个Looper

构造函数可以看出,Looper里面定义本线程的消息队列

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

Looper使用demo

    class LooperThread extends Thread {
        public Handler mHandler;
  
        public void run() {
            Looper.prepare();
  
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    // process incoming messages here
                }
            };
  
            Looper.loop();
        }
    }
  • Looper.prepare() 新建Looper对象,注意使用的是ThreadLocal
    /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    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));
    }
  • Looper.loop() 调用MessageQueue.next 得到消息,然后发给发消息的handler处理,最后回收消息recycleUnchecked
 public static void loop() {
     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();
            ... ...
     }
 }

3. Handler处理消息

  1. msg.callback:先看是不是用Handler post方法发送的Runnable,如果是,则先执行
  2. mCallback: 采用Handler handler = new Handler(callback)方式新建Handler ,传进来的callback 参数即是mCallback,如果是这种方式,则调用mCallback.handleMessage
  3. 最后调用该Handler的handleMessage
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

相关文章

网友评论

      本文标题:Handler

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