使用Handler之前必须要明白它与Looper、MessageQueue之间的联系

Looper的准备阶段:

无论是在UI线程还是在子线程,想要使用Handler就一定要先做一个准备的操作,就是创建一个Looper和MessageQueue。

1.在UI线程方面的准备工作:

在ActivityThread.java里面,程序的入口main( )里面已经调用了Looper.prepareMainLooper();

        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

在Looper类中propareMainLooper( )方法做了哪些事情?

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

在prepare(boolean )方法里面是这样的:

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

这里最后一行代码出现了一个ThreadLocal变量sThreadLocal,这个变量的作用是在每个线程保存其要使用到的变量。
再来看Looper的构造方法:

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

在Looper的构造方法里面创建了一个MessageQueue和与当前Looper相对应的Thread,并将Looper的mQueue属性设置为刚new出来的MessageQueue,将Looper的mThread属性设置为当前的Thread,在程序的入口main( )里面,此时的Thread则为UI线程,所以在这里UI线程和Looper、MessageQueue已经产生关联,就是在一进入程序的时候就已经为UI线程创建了与之相对应的Looper和MessageQueue了,这是在UI线程里面的情况。

2. 在子线程方面的准备工作:

要想在子线程中使用Handler则先要做一些准备工作,这些准备工作就是创建一个Looper和MessageQueue,在子线程使用Handler时不像在UI线程中使用Handler那样,因为UI线程在程序入口的时候就已经创建了一个Looper和MessageQueue了,如果在子线程中直接使用Handler则会报"Can't create handler inside thread that has not called Looper.prepare()"这样的异常,所以子线程要想使用Handler那也要像UI线程那样先创建一个Looper和MessageQueue。根据刚才的报错提示可以找到Looper类里面的prepare()方法。在new Handler( )之前就先调用Looper.prepare( )方法来创建一个与此子线程绑定的Looper和MessageQueue(这上步在下面的小节中有说明)。具体代码如下:

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(boolean) 调用方法,那这就跟UI线程创建Looper和MessageQueue还有绑定当前Thread一样了,只不过当前的Thread是个子线程。

小结:

无论是在UI线程的层面还是子线程层面上使用Handler都是需要准备一个Looper、MessageQueue还有将当前线程与Looper、MessageQueue进行绑定的,只不过UI线程的这些准备动作在程序入口main()那里完成的,一般情况下我们看不到这些。


Looper不断地轮循着MessageQueue:

经过了前面一步的准备工作之后,现在已经创建了一个Looper和与之相对应的MessageQueue了,前面在UI线程方面的准备工作中,看到main( )里面还有一个语句就是Looper.loop();这是在UI线程下的Looper与MessageQueue都创建好之后才调用的,这语句是让Looper开始轮循MessageQueue。

1.先让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;
    // 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
        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.isTagEnabled(traceTag)) {
            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);
        }
        // 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();
    }
}

看到第一个语句是final Looper me = myLooper();
看看myLooper()方法里面又有些什么?

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

可以看到这个方法是从当前Thread下的ThreadLocal容器中取出属于此线程的Looper。如果me为空,则会抛出异常,异常会提示说没有调用到Looper.prepare( )方法,因为如果没调用Looper.prepare( )方法,就不会去创建Looper实例,那么也就没有MessageQueue来装载Message了,所以会抛出异常。接着下一个语句:final MessageQueue queue = me.mQueue;这是从上一步中取出Looper之后,再通过Looper拿到与之相应的MessageQueue。之后会进入一个for( )循环中,此时的MessageQueue也是在不停地循环取出其内部的Message。请看接下的第2小节。

2.在loop( )中的for( )不停地调用MessageQueue.next( ):

进入到MessageQueue.next( )里面,发现很多东西都涉及到Native层,这里不作解析,只知道这个方法的主要作用是在MessageQueue中不断地将Message取出来。最后到try语句里面msg.target.dispatchMessage(msg);
这时的msg的target属性就是与之对应的Handler,最后调用Handler.dispatchMessage(msg)方法。

小结:

这里先取出当前线程所对应的Looper,然后取出该Looper里面的属性mQueue,这样就有MessageQueue可用了,最后调用MessageQueue.next( )方法将Message取出,取出的Message内有target属性,Looper会根据这个target属性来将Message派发给相应的Handler来处理Message。


创建Handler使用post或send方式发送Message到MessageQueue:

1. 首先new Handler()的位置是在UI线程还是在子线程:

先看Handler的默认构造方法:

public Handler() {
    this(null, false);
}

接着会调用到这个构造方法:

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 that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

如果是在UI线程中new Handler( )的,那么到这一步mLooper = Looper.myLooper();之后会根据当前的UI线程找到UI线程所对应的Looper,这个Looper是在main()里面通过调用Looper. prepareMainLooper()来得到Looper的,这个Looper里面已经有一个MessageQueue,并且此Looper已经与UI线程进行了绑定,所以,在UI线程new Handler()的时候,无论发送Message是在子线程中进行的还是在UI线程中进行的,结果还是在UI线程中处理Message。如果是在子线程中new Handler( )的,那么走到mLooper = Looper.myLooper();这里会抛出异常,因为这个子线程没有Looper和MessageQueue。解决方法是先在new Handler( )的前面先调用Looper.prepare()方法来创建Looper与MessageQueue,并且将此子线程与此Looper绑定,最后再调用Looper.loop()方法进行轮循MessageQueue。

2.Looper、Thread、MessageQueue、Handler之间的联系:

在Looper准备阶段,Looper已经与Thread和MessageQueue进行了绑定,当Handler对象是在UI线程中创建的,就会得到UI线程所绑定的Looper与MessageQueue,当Looper派发Message至Handler时,会在UI线程上处理Message;当Handler对象是在子线程中创建的,要先调用Looper的prepare()和loop()方法来准备一个Looper并将此Looper与此子线程进行绑定,在它们绑定的同时还会创建一个MessageQueue,然后再将此Looper轮循着这个MessageQueue,当Looper派发Message至Handler时会在子线程上处理Message。

3.使用post方式来发送Message:
public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

先看看getPostMessage(r)里面有些什么?

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

这里先调用Message.obtain()来获取Message,之所以不用new的方式来创建Message,是因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存。接着将post(Runnable r)方法里面的Runnable传给了Message的callback属性,之后等这个带有callback属性的message被派发到Handler时,Handler会根据这个callback属性来进行不同的处理操作。接着就到了sendMessageDelayed(getPostMessage(r), 0)这里了,

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

再看sendMessageAtTime (Message msg, long 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);
}

再看enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis):

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

这里会先设置Message msg的target属性,将其设置为当前的Handler,用于给Looper辨别是哪一个Handler发送了此条Message,最后一句调用了MessageQueue的enqueueMessage(Message msg, long when)方法来将Message发送到MessageQueue中去。

4.使用send方式来发送Message:
public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}

再看sendMessageDelayed(Message msg, long delayMillis):

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

再看sendMessageAtTime(Message msg, long 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);
}

最后还是会和post方式一样调用到sendMessageAtTime(Message msg, long uptimeMillis)方法,最后调用到enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)方法将Message传入MessageQueue当中,这就相当于Message入消息队列了。

小结:

Handler对象创建在UI线程上可以不用调用Looper.prepare( )方法和Looper.loop( )方法,因为这在程序入口main( )那里已经做好了;当Handler对象创建在子线程上时,一定要在创建Handler对象前面调用Looper.prepare( )和Looper.loop( )方法,只有这样做才会有与此子线程绑定的Looper和MessageQueue,并且Looper会不断地轮循着MessageQueue。另外,Handler在发送Message给MessageQueue的时候是有两种方式的,一种是post方式,另一种是send方式。post方式boolean post(Runnable r)是要带有一个Runnable参数的,千万不要以为这里创建了一个线程,因为此时并没有将Runnable作为参数放进Thread里面,实际上这个Runnable参数只是将自己赋值给了Message的callback属性而已;send方式和post方式最后都会调用到sendMessageAtTime(Message msg, long uptimeMillis)方法然后通过enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
方法的里面的最后一句MessageQueue.enqueueMessage(Message msg, long when)来将Message根据所设置的时间参数来进行入队,时间参数越小就越早MessageQueue。


Looper会将从MessageQueue里面拿出来的Message根据其target属性向指定的Handler派发消息:

经过上一步的分析,发现Handler的两种发送Message方式最后都会调用到sendMessageAtFrontOfQueue(Message msg)和enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis),其中enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)方法里面:

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

第一行就设置了Message的target属性了,就是将当前的Handler赋值给了Message的target属性。然后到Looper.loop()方法里面

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

有这么一个语句,这就是根据Message的target属性来调用dispatchMessage(Message)方法的,此时的target属性指的是发送该Message的Handler。

小结:

Looper之所以能精准地派发Message到Handler手里,是因为在将Message入队之前就为其标记好了相对应的Handler了,在Handler类里面的enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)方法中为Message设置了target属性,属性值为当前的Handler,这个就是标记,所以到Looper轮循派发Message的时候会根据这个标记来调用Handler里面的dispatchMessage(Message msg)方法。


Handler根据post或send方式来处理Message:

Looper在上一步中根据Message的target属性来派发Message至相应的Handler之后,Handler是如何处理Message的?

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

先看if语句里面,Message的callback属性不为null,则调用handleCallback(Message message)方法。这个Message的callback属性是什么时候添加的呢?还记不记得在Handler里面使用post方式来发送Message的时候

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

再看getPostMessage(Runnable r)方法内部:

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

原来Message的callback属性是在使用post方式发送Message时所设置的Runnable啊!如果此时是使用Handler的post方式来发送Message,那么在Looper派发完Message之后会调用Handler的handleCallback(Message message)方法

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

前面讲post方式来发送Message的时候提到post(Runnable r)方法里面的Runnable参数,这不是在创建一个线程,因为在handleCallback(Message message)方法里面是直接调用了这个Runnable的run()方法,这是不会创建线程或启动线程的,所以有时候为了方便是会这样写post方式发送Message的:

handler_post.post(new Runnable() {
    @Override
    public void run() {
        Log.d("post",Thread.currentThread().getName());
        tv_show.setText("RUOK?");
    }
});

如果使用send方式发送Message,则会走dispatchMessage(Message msg)
的else里面,

if (mCallback != null) {
    if (mCallback.handleMessage(msg)) {
        return;
    }
}

这里面还有一个Handler的mCallback属性,如果new Handler()的时候选用Handler的默认构造方法,则此mCallback属性则为null,所以最后处理Message的时候会走到这一句handleMessage(msg);再走进发现public void handleMessage(Message msg) {}是一个空方法,这就是我们通常使用send方式来发送Message,最后还要重写handdleMessage(Message msg)方法的原因了,以下是handleMessage(Message msg)的最基本用法:

Handler handler_send = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        Log.d(TAG,Thread.currentThread().getName());
        String show =(String) msg.obj;
        tv_show.setText(show);
    }
};
小结:

如果先前使用post方式来发送Message,则在处理Message的时候可以直接在post方式的Runnable参数那里直接更改UI,如果先前使用send方式来改善Message,则在创建Handler对象的时候要重写HandlerMessage(Message msg)方法来更新UI。


一个总体的流程:

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

推荐阅读更多精彩内容