记一次讨论总结,帮助你和Handler完全作一次告终

最近在群里的几个好友一块儿讨论技术的时候说到了Handler,下面是对此次讨论的总结,此次讨论主要是对handler.post(Runnable r)这个方法来进行展开的,不论是handler.post(Runnable r)仍是handler.sendMessage(msg);其内部实现都是同样的,就不重复分析了。不过相信通过此次总结你能对Handler机制来一次完全的告终。bash

一:handler.post(Runnable r)开启了一个子线程(错误

这是一个错误的观点,其实一部分人出现这个观点的缘由是由于将Runnable和Thread的概念搞混淆了。Runnable只是开启线程的一种方式,并非开启了一个线程。为何说这个观点是错误的,下面代码结果打印一下就知道了。less

Handler handler = new Handler();
Log.e("======>>>>>>>", "Thread: " + Thread.currentThread().getName());
handler.post(new Runnable() {
    @Override
    public void run() {
    Log.e("------>>>>>>>", "Thread: " + Thread.currentThread().getName());
    }
});
复制代码

结果以下:不论是在里面仍是在外面都是显示主线程,因此handler.post(Runnable r)并无开启一个线程 async

为何会这样呢?那就只能去源码里面找缘由了,咱们点击post()这个方法进去

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

这里很容易理解就是发送一个延时的消息。继续点进去ide

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

咦,sendMessageDelayed(Message msg, long delayMillis)的第一个参数是Message,那这个Message是哪里的来的,咱们返回去看,有个getPostMessage(r)的方法,看看里面干了啥?oop

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}
复制代码

很简单的代码,就是生成了一个Message对象并将Runnable这个对象赋值给m.callback并将生成的这个Message对象返回去。回去继续看sendMessageDelayed()。点sendMessageAtTime()方法进去看看post

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);
}
复制代码

这里获取了一个消息队列,经过enqueueMessage()方法名能够猜测应该是将Message这个对象加入队列中。继续点进去ui

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码

这里须要注意一下msg.target = this;这句代码。继续往下看,如今看看MessageQueue里面的enqueueMessage()这个方法干了什么。this

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 do not 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;
}
复制代码

参数msg是由咱们传进去的Runnable转换成的对象,when时是执行时间,当咱们调handler.post 时该值为0,细心的朋友会发现mMessages这个对象,咱们能够把它理解为是待执行的message队列,该队列是按照when的时间排序的且第一个消息是最早执行。代码中有一个if中有三个条件:若是mMessages对象为空,或者when==0,或者新消息的when时间比mMessages队列中的when时间还要早,符合以上一个条件就把新的msg插到mMessages的前面并把next指向它,等待loop的轮询。不然经过一个for的死循环遍历已有的message对象,其中if语句when < p.when,when是新消息的执行时间,p.when的是队列中message消息的执行时间,若是找到比新的message还要晚执行的消息,就执行 msg.next = p;prev.next = msg;也就是插到该消息的前面,优先执行新的消息。这里不难看出就是将消息加入到消息队列中。spa

上面说到将消息加入到消息队列中,那这个Handler的mQueue是哪里来的呢?经过查找不难发现mQueue只有两处被赋值,且都是被looper.mQueue赋值。因此Handler中的mQueue就是Looper的mQueue。再去Looper找,mQueue是在Looper的构造方法中被初始化线程

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

经过这个构造方法咱们能够知道,一个Looper对应一个MessageQueue

既然存消息了,那取消息在哪里呢?咱们稍微想一下就知道了,要拿到MessageQueue消息队列中的Message对象确定会有个方法,并且这方法确定是返回Message对象,查看一下MessageQueue里面的全部方法能够找到一个next()的方法。点一下,咦,没反应咋办。别急,想想Handler机制就这个几个类,确定逃不过这几个类,就去这几个类搜就就好了,发现只有Looper在调用这个方法。

for (;;) {
    Message msg = queue.next();
    ···
    msg.target.dispatchMessage(msg);
    ···
}
复制代码

这是Looper.loop()在调用。代码较多就不粘出来了,在源码里咱们看到这么两行,经过方法名能够知道分发消息的意思。那msg.target是啥呢?感受像看到过呢?返回去找一下不难发如今Handler的enqueueMessage()中就有msg.target = this;这一行。那dispatchMessage()是干啥的呢?去Handler类中找这个方法。

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

一眼就能明白的代码,就是经过msg.callback为不为空来判断,经过上面分析咱们知道msg.callback是不为空的。啥?你是怎么知道msg.callback怎么不为空的?提问的这位兄弟麻烦你回过头去从新再来一遍吧。在上面分析getPostMessage(r)方法时不是就有m.callback = r;这么一行么。因此如今知道代码是往handleCallback(msg);这里走的,进去瞧瞧

private static void handleCallback(Message message) {
    message.callback.run();
}
复制代码

好想来句:shit!!!原来就是r.run()啊!这不就是接口方法调用么。咱们实际每次用的handler.post(Runnable r)就是对这个Runnable这个接口具体的实现而已。这和咱们自定义一个点击事件有啥区别呢?因此不难看出Runnable就是一个普通到不能再普通的接口而已。通过上面的分析咱们不只知道了Runnable只是个普通的接口并且还一并弄清楚了Handlers是如何存消息取消息的。

经过分析Thread同样能够得出Runnable就是一个普通的接口。这里就不作深刻分析了

new Thread(new Runnable() {
    @Override
    public void run() {
    Log.e("------>>>>>>>","Thread: "+Thread.currentThread().getName());
    }
}).start();
复制代码

点进去看,能够看到Runnable直接赋值给了成员变量Runnable target了,而target.run()在Thread的run()方法中,当调用start()后会致使线程的run()方法被执行。那么也就能够证明Runnable只是个普通接口了,是不会开启线程的。

二:Handler是如何实现线程切换的?

Handler handler = new Handler();
Log.e("++++++>>>>>>>","Thread: "+Thread.currentThread().getName());
new Thread(){
    @Override
    public void run() {
        super.run();
        Log.e("======>>>>>>>","Thread: "+Thread.currentThread().getName());
        Looper.prepare();
        handler.post(new Runnable() {
            @Override
            public void run() {
            Log.e("------>>>>>>>","Thread: "+Thread.currentThread().getName());
            }
        });
        Looper.loop();
    }
}.start();

········

Log.e("++++++>>>>>>>","Thread: "+Thread.currentThread().getName());
new Thread(){
    @Override
    public void run() {
        super.run();
        Log.e("======>>>>>>>","Thread: "+Thread.currentThread().getName());
        Looper.prepare();
        new Handler().post(new Runnable() {
            @Override
            public void run() {
            Log.e("------>>>>>>>","Thread: "+Thread.currentThread().getName());
            }
        });
        Looper.loop();
    }
}.start();
复制代码

分别打印结果以下

结果1

结果2
从结果1和结果2的比较能够看出Handler从主线程切换到子线程中,而那Handler是如何切换线程的呢?从上面的代码看只是初始化Handler的位置不一样而已。那咱们来看看初始化Handler的过程干了啥。

public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            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());
            }
        }

        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;
        mCallback = callback;
        mAsynchronous = async;
    }
复制代码

在这里咱们能够看到当mLooper为空时会抛出一个异常,意思就是在当前线程没有调用Looper.prepare()时是不容许建立Handler的,也就是不能new Handler()。这说明真正起到关键性做用的是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));
}
复制代码

很简单就是建立一个Looper并将这个looper设置到ThreadLocal中。看看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);
}
复制代码

看到这里应该能明白了,这个set()方法就是把当前线程和这个new出来的Looper绑定起来。而在咱们初始化Handler的时候不知道有没有注意到一句 mLooper = Looper.myLooper();

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
···

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();
}
复制代码

这里很好理解就是经过当前线程(key)来取出与当前线程绑定的Looper(value) 。这也正好和Looper.perpare()方法里的存保持一致。因此能够知道Handler里面的Looper对象就是以前Looper.prepare()中建立出来的Looper了。综合上面对一的分析,就是在调用Looper.perpare()的时候将当前线程、消息队列和该Looper对象关联起来了。这样就能够解释了:Looper调用prepare()将Looper绑定到当前线程,当调用Looper.loop()时开启循环轮询消息,因此在当前线程调用Handler.post(),最终都会在指定线程被Looper.loop()轮询出来。

三:handler.post(Runnable r)为何能在子线程操做UI

看了上面第二点应该有部分人应该已经知道缘由了。其实咱们当初讨论的时候是从第三点开始的,这里之因此先从二开始讲是能更深入理解为何咱们经常使用的handler.post(Runnable r)就能在子线程操做UI。

在第二点中咱们知道了两点影响因素,Looper.perpare()和new Handler(),当在主线程中去初始化一个Handler对象时咱们并无调用Looper.perpare()和Looper.loop(),那Looper是哪里来的呢?经过对mLooper = Looper.myLooper(); 的分析咱们知道了,Looper其实就是当前线程的Looper,而咱们又是在UI线程初始化的Handler,因此咱们在new Handler()时拿到Looper就会是与UI线程绑定的Looper,通过上面一和二的分析,咱们已经知道了当前线程,Looper和MessageQueue是相互关联起来的。因此UI线程初始化的Handler发送的消息最终会被UI线程的Looper.loop()轮询出来。而这个Looper是在咱们初始化UI线程时,系统就已经帮咱们建立好的,因此咱们才会不须要调用UI线程的Looper.perpare()和Looper.loop()。

总结

经过调用Looper.perpare()将当前线程和Looper以及MessageQueue关联起来,Handler发送一个Message对象,并将这个Message对象经过enqueueMessage()方法存到MessageQueue消息队列中,当调用Looper.loop()方法时就会去MessageQueue中轮询消息取出Message对象,将取出的Message经过Handler的dispatchMessage()方法对Message进行处理。而这个dispatchMessage()会根据你传进来的是Runnable仍是Message判断到底最后是调用runnable.run()仍是handleMessage(msg),从而实现外部建立Handler对象的具体实现。

相关文章
相关标签/搜索