不要再问我Handler机制了!

前言

我们都知道由于Android开发规范的限制,我们并不能在子线程中访问UI组件,否则便会触发程序异常崩溃,因此Andorid引入了Handler机制,通过它可以轻松地将一个任务切换到Handler所在的线程中去执行,实现多线程间的消息通信功能。本篇文章旨在通过深入浅析Handler机制的源码来了解它的通信过程,并穿插解惑常见的面试题,好了,让我们开始吧!

Question

1、Handler机制的原理。
2、如何避免实例化Handler造成的内存泄漏问题?
3、MessageQueue是什么时候创建的?主线程有多个Handler时,对应创建多少个MessageQueue?
4、有延时消息处理时,MessageQueue怎么处理Message?
5、Looper的工作原理?
6、ThreadLocal的作用?
7、如何在子线程里使用Handler?
8、Looper为什么不会造成线程阻塞?

Handler

  • Constructor
    我们要使用Handler首先要得到一个Handler对象,那么我们就从最简单的Constructor作为入口,来分析它的源码。
public Handler() {
    this(null, false);
}

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {  // 潜在内存泄漏的检查,默认为false。
        //匿名类、内部类或本地类都必须申明为static,否则会警告可能出现内存泄露。
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
            (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
    // 使用 Looper 的静态方法 myLooper() 来获取当前线程的 Looper
    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;  //消息队列,来自Looper对象
    mCallback = callback;  //回调方法
    mAsynchronous = async;  //设置消息是否为异步处理方式
}
public Handler(Looper looper) {
   this(looper, null, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

OK,从上面这一小段源码中我们可以发现两个问题。
1、声明Handler非静态的匿名类、内部类和本地类时,可能会造成内存泄;
在Java中非静态内部类或者匿名内部类都是默认持有外部类的引用的,因此非静态匿名类Handler会默认持有Activity的引用,这样当Activity销毁时,如果Handler有任务正在处理未及时释放Activity的引用便会造成内存泄漏。现在我们知道造成内存泄漏的原因,那么我们将Handler声明为静态内部类并配合SoftReferenceWeakReference就可以避免内存泄漏,这也解答了Question-2

private static class NoLeakHandler extends Handler{
    private WeakReference<Activity> mActivity;

    public NoLeakHandler(Activity activity){
        mActivity = new WeakReference<>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
}

2、当前线程没有Looper或者没有调用Looper.prepare()时,会触发运行时异常;
为什么在子线程中声明Handler会触发程序异常,而主线程不会?下面是ActivityThread的部分源码。

//创建一个消息循环
public static void main(String[] args) {
    ......
    Looper.prepareMainLooper();  //主线程的Looper(),也是app应用主要的消息机制的消息管理者
    ......
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

可以看到ActivityThread调用了Looper.prepareMainLooper()为我们创建了Looper,而子线程默认没有创建Looper,这也是我们在子线程创建Handler会报错的原因,所以需要我们手动调用Looper.prepare()创建Looper,避免触发程序异常。
子线程中创建Handler的第一种方式:

private Handler mHandler;
//创建子线程handler
private void createHandler(){
    new Thread(new Runnable() {
        @Override public void run() {
            Looper.prepare();
            mHandler = new Handler(Looper.myLooper());//传入子线程Looper
            Looper.loop();
        }
    }).start();
}

第二种方式是使用HandlerThread

private Handler mHandler;
//创建子线程handler
private void createHandler(){
    HandlerThread handlerThread = new HandlerThread("myHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
    handlerThread.start();//必须开启
    mHandler = new Handler(handlerThread.getLooper());
}
private void postRun(){
    mHandler.post(new Runnable() {
        @Override public void run() {
            SystemClock.sleep(5000);
            MainActivity.this.runOnUiThread(new Runnable() {
                @Override public void run() {
                    txt_result.setText("完成操作");
                }
            });
        }
    });
}

HandlerThread内部创建消息队列,外部通过handler通知HandlerThread执行,HandlerThread源码如下:

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private @Nullable Handler mHandler;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

    @NonNull
    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }
    ······
}

HandlerThread其实就是对方式一的封装,并内置了Handler,使用起来更简单。其中HandlerThread构造方法的第二个参数表示优先级(范围-20~19,越小优先级越高)

  • dispatchMessage()
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {  // Message的 callback 是一个 Runable 对象
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
private static void handleCallback(Message message) {
    message.callback.run();
}
public interface Callback {
    public boolean handleMessage(Message msg);
}

dispatchMessage()首先检查了Messagecallback是否为null,不为null就通过handleCallback来处理消息,其次检查mCallback是否为null,不为null就调用mCallbackhandleMessage(msg)处理消息,最后调用HandlerhandleMessage()来处理消息。

Looper

来看看Looperprepare()prepareMainLooper()

public static void prepare() {
    prepare(true);
}
private static void prepare(boolean quitAllowed) {
    //每个线程只允许执行一次该方法,第二次执行时线程的TLS已有数据,则会抛出异常。
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //创建Looper对象,并保存到当前线程的TLS区域
    sThreadLocal.set(new Looper(quitAllowed));
}
//这个方法是主线程Looper初始化的代码,入口在ActivityThread中,即App初始化时就会被调用
public static void prepareMainLooper() {
    prepare(false); //设置不允许退出的Looper
    synchronized (Looper.class) {
        //将当前的Looper保存为主Looper,每个线程只允许执行一次。
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

可以看到在prepare()中实例化了Looper并将其保存在ThreadLocal中,且每个线程只允许执行一次ThreadLocalget(),也就是说一个Thread只能有一个Looper。而MessageQueue对象是在实例化Looper时创建的的,由此我们可以得出一个结论:一个Thread只能有一个Looper和一个MessageQueue,所以在主线程中不管存在多少个Handler,都只共用一个MessageQueue(这也正好解答了Question-3)。

再来看主线程代码,ActivityThread不仅调用了prepareMainLooper(),还调用到了loop(),那loop()是干什么的?

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
         hrow new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue
    for (;;) {
        Message msg = queue.next();  // 可能阻塞
        if (msg == null) {  // 没有消息指示消息队列正在退出。
            return;
        }
       msg.target.dispatchMessage(msg);  //  调用 Handler 的 dispatchMessage 方法
       msg.recycleUnchecked();
    }
}

可以看到Looper相当于是一个消息轮询器,它通过loop()不断地调用了MessageQueuenext方法来获取新消息,只有next()返回null,才能终止循环。而 next() 是一个阻塞操作,当没有消息时,next()会一直阻塞在那里,这也导致了loop()一直阻塞。如果next()返回了新消息,Looper 就会调用msg.target.dispatchMessage(msg)处理这条消息;而msg.target指的是发送这条消息的Handler对象,这样 Handler 发送的消息最终又交给了它的 dispatchMessage方法来处理。

ThreadLocal

上面提到Looper都保存在了ThreadLocal中,那ThreadLocal到底是什么呢?

1.定义

Implements a thread-local storage, that is, a variable for which each thread
has its own value. All threads share the same {@code ThreadLocal} object,
but each sees a different value when accessing it, and changes made by one
thread do not affect the other threads. The implementation supports
{@code null} values.

大致意思是实现了与一个线程相关的存储,即每个线程都有自己独立的变量。所有的线程都共享一个ThreadLocal对象,并且当一个线程的值发生改变之后,不会影响其他的线程的值。

2.实现

ThreadLocal的类定义使用了泛型ThreadLocal<T>,其中T指的是在线程中存取值的类型。(对应Android中使用的ThreadLocal, TLooper

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

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}
  • get方法
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);  //以当前线程为 key 得到 map 中的 Entry
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

private T setInitialValue() {
    T value = initialValue();  // null;
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);  //如果当前线程的 map 已经存在,set 值为 null
    else
        createMap(t, value);
    return value;
}
  • ThreadLocalMap
static class ThreadLocalMap {
   
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }

    // map 初始容量 16,必须为 2 的幂
    private static final int INITIAL_CAPACITY = 16
    private Entry[] table;  // Entry表,大小必须为2的幂
    private int size = 0;
    private int threshold;  //下次需要扩容的阈值,默认 0
    private void setThreshold(int len) {
       threshold = len * 2 / 3;   // 2/3 的负载因子
    }

    ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
        //初始化 table,大小 16
        table = new Entry[INITIAL_CAPACITY];
       //用第一个健的哈希值对初始大小取模得到索引,和 HashMap 的位运算代替取模原理一样
        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
        table[i] = new Entry(firstKey, firstValue);  //初始化 Entry
        size = 1;  //第一个值进入 table,table 大小置 1
        setThreshold(INITIAL_CAPACITY);  //设置阈值
    }

   private Entry getEntry(ThreadLocal<?> key) {
        int i = key.threadLocalHashCode & (table.length - 1);  //拿到 table 索引
        Entry e = table[i];
        if (e != null && e.get() == key)
            return e;
        else
            return getEntryAfterMiss(key, i, e);  //线性探测继续寻找
    }
    private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
        Entry[] tab = table;
        int len = tab.length;

        while (e != null) {
            ThreadLocal<?> k = e.get();
            if (k == key)
                return e;]
            if (k == null)
                expungeStaleEntry(i);  //如果拿到一个 null 的 key 说明已经被回收了,需要清理
            else
                i = nextIndex(i, len);  //否则的话说明需要继续寻找
            e = tab[i];
        }
        return null;
    }

   private int expungeStaleEntry(int staleSlot) {
        Entry[] tab = table;
        int len = tab.length;

        tab[staleSlot].value = null;   // 将 table[i] 这个引用和 value 置 null
        tab[staleSlot] = null;
        size--;   //table 大小减一

        Entry e;
        int i;
        // 遍历清理
        for (i = nextIndex(staleSlot, len);
                (e = tab[i]) != null;
                i = nextIndex(i, len)) {
            ThreadLocal<?> k = e.get();
            //遇到还可以清理的话顺便清理掉
            if (k == null) {
                e.value = null;
                tab[i] = null;
                size--;
            } else {
                //遇到还没被回收的,rehash 找到新的为空的索引位置
                int h = k.threadLocalHashCode & (len - 1);
                if (h != i) {
                    tab[i] = null;  // 将原位置置 null
                    while (tab[h] != null)  // 找到新位置
                        h = nextIndex(h, len);
                    tab[h] = e;
                }
            }
        }
        return i;
    }
}

3.ThreadLocal的工作原理

ThreadLocal通过获取当前线程中的ThreadLocalMap,从而实现每个单独线程的信息绑定。Android的消息机制中,Looper便是采用ThreadLocal作为存储结构,所以Looper对象的存储只会在当前线程中,子线程若是使用消息机制的话,必须调用Looper.prepare()来在线程中新建一个Looper对象(Question-6)。

消息分发

  • 消息发送
public final boolean sendMessage(Message msg) {
    return sendMessageDelayed(msg, 0);
}

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

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

public final boolean postAtTime(Runnable r, long uptimeMillis){
    return sendMessageAtTime(getPostMessage(r), 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);
}

可以看到Message最终会被发送到MessageQueueenqueueMessage()中去,并指定msg.target为当前发送Message的Handler,由此可知MessageQueue是用来存放Message的消息队列。

  • 消息回收
public final void removeMessages(int what) {
    mQueue.removeMessages(this, what, null); 
}
public final void removeCallbacksAndMessages(Object token) {
     mQueue.removeCallbacksAndMessages(this, token);
 }

实质上都是调用的MessageQueue的方法来达到目的的

MessageQueue

MessageQueue是消息机制的Java层和C++层的连接纽带,大部分核心方法都交给native层来处理,其中比较重要两个方法。

private native void nativePollOnce(long ptr, int timeoutMillis);  //阻塞loop循环
private native static void nativeWake(long ptr);                  //唤醒loop循环
  • Constructor
MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

接收一个参数quitAllowed,决定当前队列是否允许被终止。同时调用 一个native方法,初始化了一个long类型的变量mPtr

  • enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
     f (msg.target == null) {  // msg.target 为 Handler
        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.
            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的实现来看,尽管Messagequeue叫做消息队列,但是它的内部实现确实通过一个单链表的数据结构来维护消息列表的,这是因为单链表在插入和删除上比较有优势。
解惑Question-4:
我们再来看下新消息需要放到队头的情况:p == null || when == 0 || when < p.when。即队列为空,或者新消息需要立即处理,或者新消息处理的事件比队头消息更早被处理。这时只要让新消息的next指向当前队头,让mMessages指向新消息即可完成插入操作。也就是说MessageQueue的先进先出原则指的并不是插入的顺序,而是Message执行时间的顺序

  • next()
    相信大家一定没有忘了next()是在Looper.loop()中调用的吧,现在我们来看一下它的源码。
Message next() {
    ·····
    int pendingIdleHandlerCount = -1;
    int nextPollTimeoutMillis = 0;
    for (;;) {
        ·····
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    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 {
                nextPollTimeoutMillis = -1;
            }

            if (mQuitting) {
                dispose();
                return null;
            }
            ·····
        }
       ·····
        pendingIdleHandlerCount = 0;
        nextPollTimeoutMillis = 0;
    }
}

可以发现next()是一个无限循环的方法,如果消息队列中没有消息,那么next()会一直阻塞在这里。当有新消息来临时,next()会返回这条消息并将其从单链表中移除。
大致思路如下:先获取第一个同步的message。如果它的when不晚与当前时间,就返回这个message;否则计算当前时间到它的when还有多久并保存到nextPollTimeMills中,然后调用nativePollOnce()来延时唤醒,唤醒之后再照上面那样取message,如此循环。代码中大部分都是对链表的指针操作,其他的逻辑很清楚,就不一句句分析了。

  • removeMessages()
    该方法有2个重载,这里以void removeMessages(Handler h, int what, Object object){}方法为例。
void removeMessages(Handler h, int what, Object object) {
    if (h == null) {
        return;
    }
    synchronized (this) {
        Message p = mMessages;
        // Remove all messages at front.
        while (p != null && p.target == h && p.what == what
               && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }

        // Remove all messages after front.
        while (p != null) {
            Message n = p.next;
            if (n != null) {
                if (n.target == h && n.what == what
                    && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}

首先是Handler的非空判断,然后便是同步代码块,里面只有两个while循环。为什么有两个呢?这是因为在数据结构中链表分两种:带头结点和不带头结点。而这两种链表的遍历方式有所不同:不带头结点的链表中,第一个元素需要单独处理,然后才能将后续部分当做带头结点的链表来使用while循环遍历。可以看出MessageQueue是不带头结点的链表,而且遍历过程中有需要删除节点,因此要特殊处理的不只是第一个元素,而是第一组符合删除条件的元素。

  • quit()
void quit(boolean safe) {
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }

    synchronized (this) {
        if (mQuitting) {
            return;
        }
        mQuitting = true;

        if (safe) {
            removeAllFutureMessagesLocked();  //清空延迟消息
        } else {
            removeAllMessagesLocked();  //清空所有消息
        }

        // We can assume mPtr != 0 because mQuitting was previously false.
        nativeWake(mPtr);
    }
}

可以看到quit()最终会调用到removeAllFutureMessagesLocked()或者removeAllMessagesLocked()两个方法,我们再来看下这两个方法的实现。

  • removeAllFutureMessagesLocked() & removeAllMessagesLocked()
private void removeAllFutureMessagesLocked() {
    final long now = SystemClock.uptimeMillis();
    Message p = mMessages;
    if (p != null) {
        if (p.when > now) {  //如果队头时间比当前时间大,则说明所有时间都是延迟的,那么全部删除 
            removeAllMessagesLocked();
        } else {
            Message n;
            for (;;) {  //找出大于当前时间的那个节点 
                n = p.next;
                if (n == null) {
                    return;
                }
                if (n.when > now) {
                    break;
                }
                p = n;
            }
            //删除后部分节点 
            p.next = null;
            do {
                p = n;
                n = p.next;
                p.recycleUnchecked();
            } while (n != null);
        }
    }
}

private void removeAllMessagesLocked() {
    Message p = mMessages;
    while (p != null) {
        Message n = p.next;
        p.recycleUnchecked();
        p = n;
    }
    mMessages = null;
}

这段代码比较清晰,如果队头时延时消息时则调用removeAllMessagesLocked清空所有的Message,如果没有则继续遍历下一个节点,直到找到执行时间大于当前时间的那个节点,再将含这个节点之后的所有节点删除。

Question:Looper.loop()为什么不阻塞主线程?
还记得下面这两个方法吗?

private native void nativePollOnce(long ptr, int timeoutMillis);  //阻塞loop循环
private native static void nativeWake(long ptr);                  //唤醒loop循环

Looper通过调用MessageQueue.next()取消息时,如果已经没有消息了,此时该线程便会阻塞在next()nativePollOnce()中,主线程便会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生时,才通过调用nativeWake()pipe管道写端写入数据来唤醒主线程工作。这里涉及到的是Linuxpipe/epoll机制,epoll机制是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。
参考资料 Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

Handler机制的原理

Andriod提供了HandlerLooper来满足线程间的通信。Looper类用来管理特定线程内对象之间的消息交换。
1、Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里的Message Queue
2、Handler: 可以构造Handler对象来与Looper沟通,以便push新消息到MessageQueue里;或者接收LooperMessage Queue取出来的消息。
3、MessageQueue:用来存放Message,按照Message执行时间的顺序对Message进行排列。
4、线程:UI thread通常就是ActivityThread,而Android启动程序时会替它建立一个MessageQueue

Handler机制时序图

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容

  • 文//夏玫小墨 夏天来了,夏天真的来了,时间总是过得这么让人不由得感叹时间都去了哪里,它却偷偷地在我们指尖划过,速...
    夏玫小墨阅读 314评论 4 5
  • 随笔三则 (一)容纳不完美的自己 人很多时候要能容纳不完美的自己...
    幸运的颜色阅读 209评论 2 0
  • 如果生活可以轻而易举,你还会坚持不懈拼命奋斗吗?如果人生可以快意恩仇,你还会墨守成规忍受不公吗? 2019年一家新...
    海盗蒸鸡蛋阅读 189评论 0 0
  • 沈星问林奕华,你那么懂得爱,那么会爱,那么会经营爱,怎么还会失恋呢? 林奕华回答说,当一本爱上一本杂志的时候书就会...
    霜巍阅读 158评论 0 1