Android消息机制分析:Handler、Looper、MessageQueue源码分析

1.前言

关于Handler消息机制的博客实际上是非常多的了。
之前也是看别人的博客过来的,但是过了一段时间之后,一些细节也就忘了。
所以,就自己撸一篇,权当笔记,方便以后翻阅。

这篇文章主要是分析Handler消息机制原理以及收集一些面试题来讲解,熟悉的话可以不用看了。

本文源码基于android 27

2.Android消息机制概述

2.1 本质

Android的消息机制本质就是一套消息发送,传递及处理的机制。

2.2 角色说明

角色 作用
Handler
(处理者)
负责消息的发送及处理等等。
Message
(消息)
保存消息的内容,如保存一个消息类型(what)等等。
MessageQueue
(消息队列)
保存Message的数据结构,是一个链表。
Looper
(循环器)
从消息队列中循环的取出消息然后把消息交给Handler处理。

2.3 原理简介

消息机制在Android上的流程为:

  1. 应用启动时会在主线程中创建一个Looper(循环器),Looper(循环器)内部则会创建一个MessageQueue(消息队列);
  1. 然后Looper(循环器)就会不断的轮询MessageQueue(消息队列)中是否有Message(消息);
  2. 我们可以通过Handler去发送一个Message(消息),发送之后Message(消息)就会进入到MessageQueue(消息队列)中去,Looper(循环器)通过轮询取出Message(消息),然后交给相应的Handler(处理者)去处理。

下面会通过分析源码来验证这个过程。

上面这个流程总结成一句话就是:

Looper(循环器)通过不断的从MessageQueue(消息队列)中取出Message(消息),然后交给相应的Handler(处理者)去处理。

是不是很简单呢?

2.4 Handler的应用--UI更新

对于消息机制,我们平常接触的最多的场景就是:

在子线程中进行一些数据更新等耗时操作,然后使用Handler在主线程中去更新UI。

为什么要怎么操作呢?这里有两个前提:

1.Android开发中规定了UI的更新只能在主线程中去操作,在子线程中更新会报错。
2.我们在主线程中创建的Handler能够接受到同一个Handler在子线程中发送的消息。

可以看到,在这种场景下我们使用Handler的目的就是切换到主线程中去更新UI。而Handler的使用方式是很简单的,这里就不写例子了。

那么,为什么更新UI只能在主线程中去操作呢?

这是因为Android中的UI控件不是线程安全的,因此在多线程中并发,可能会出现线程安全的问题,即访问UI控件可能会出现跟预期不一样的结果。那么为什么不使用锁机制呢?因为加锁会降低访问UI的效率,锁机制会阻塞某些线程的执行。因此,最简单高效的方法就是使用单线程模型来进行UI的访问了。

那么,为什么主线程中的Handler能接受到其他线程发来的消息呢?
这是后面源码分析的内容,这里暂且不表。

2.5 Handler的其他应用

上面UI更新实际上只是消息机制其中一个应用场景。
如果我们了解四大组件的启动停止等过程的话,就会发现,都是在一个名为HHandler中处理状态切换等逻辑,这个HActivityThread的内部类。其本质就是切到主线程中去处理。

所以说,不要将Handler仅仅局限于UI更新。

3.源码分析

本节主要深入源码对消息机制进行分析。对涉及到LooperMessageQueueMessageHandler等类进行逐一分析。

3.1 Looper类

3.1.1 Looper(循环器)的创建

Looper的创建可以分为在主线程中创建以及在子线程中创建,我们分别来看下。

3.1.1.1 主线程中创建Looper

先来看下Looper在主线程中是什么时候创建的。

3.1.1.1.1 ActivityThread的main()

应用启动时,会调用到ActivityThread中的main()方法,这个main()方法是应用程序的入口。main()里面会创建一个Looper对象出来。我们来看下代码:

    public static void main(String[] args) {
        //省略无关代码

        //为主线程创建1个Looper对象
        Looper.prepareMainLooper();
        
        //创建主线程
        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        //省略无关代码
        
        //开启消息循环
        Looper.loop();
    }

可以看到,应用启动时就为主线程创建出一个Looper对象,并且开启消息循环。

3.1.1.1.2 Looper的prepareMainLooper()

再来看下prepareMainLooper()

    public static void prepareMainLooper() {
        //最终还是调用prepare()
        //参数false表示主线程中的消息循环不允许退出
        prepare(false);

        //判断sMainLooper是否为null,否则抛出异常
        //即prepareMainLooper()不能够被调用两次
        //保证了主线程中只存在一个Looper对象
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
3.1.1.1.3 Looper的prepare()

再来看下prepare()方法:

    private static void prepare(boolean quitAllowed) {
        //ThreadLocal可以保存一个线程内的局部变量
        //这里判断当前线程是否已经存在Looper对象,存在的话则抛异常
        //因为一个线程只能创建一个Looper对象
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //当前线程没有创建Looper对象的话
        //则新创建一个Looper对象
        //并把这个Looper对象保存到ThreadLocal中
        sThreadLocal.set(new Looper(quitAllowed));
    }

prepare()中就是创建一个Looper对象并把Looper对象保存到线程中的ThreadLocal

3.1.1.1.4 Looper的构造方法

再来看下Looper的构造方法:

    private Looper(boolean quitAllowed) {
        //创建消息队列
        mQueue = new MessageQueue(quitAllowed);
        //记录当前线程.
        mThread = Thread.currentThread();
    }

Looper内部中就是创建了一个消息队列。

3.1.1.1.5 小结

应用启动时,主线程会创建一个Looper对象出来,Looper内部则创建消息队列。

3.1.1.2 子线程中创建Looper

在子线程中创建Looper非常简单,直接看例子吧:

3.1.1.2.1 子线程中创建Looper例子
class LooperThread extends Thread {
    public Handler mHandler;
 
    public void run() {
        Looper.prepare();
 
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                //TODO ...
            }
        };

        Looper.loop(); 
    }

调用一下无参数的prepare()方法即可。

3.1.1.2.2 Looper的prepare()
    public static void prepare() {
        prepare(true);
    }

最终还是调用有参数的prepare()方法,有参数的prepare()方法祥见上面的代码分析。
true代表这个Looper是可以退出的。主线程中创建的Looper则是不能退出的。这就是他们的区别。

3.1.2 Looper(循环器)的消息循环

Looper是通过loop()这个方法来进行消息循环的,我们来看下代码:

3.1.2.1 Looper的loop()
    public static void loop() {
        //获得当前线程的Looper对象
        //myLooper()实际上通过sThreadLocal.get()来获取的
        final Looper me = myLooper();
        //如果当前线程没有创建过Looper,则抛出异常
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //获得Looper对象中的消息队列
        final MessageQueue queue = me.mQueue;

        //死循环
        for (;;) {
            //从消息队列中取出一个消息
            //如果消息队列没有消息的话,会阻塞在这里
            Message msg = queue.next(); // might block
            //消息为null的话表示停止消息循环
            //可以通过queue.quit()来停止,前提是通过prepare(true);来创建的
            //主线程中不允许停止消息循环
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            //...
            
            //分发消息去处理
            //msg.target就是要处理的Handler,祥见后面分析
            msg.target.dispatchMessage(msg);

            //...

            //回收消息
            msg.recycleUnchecked();
        }
    }
3.1.2.2 小结

Looper中通过一个死循环从消息队列中取消息,一旦取到消息之后,就分发交给Handler来处理。

3.1.3 Looper(循环器)的退出

    public void quit() {
        mQueue.quit(false);
    }
    
    //安全退出
    public void quitSafely() {
        mQueue.quit(true);
    }

退出Looper有两个方法,如上。最终还是通过调用MessageQueue.quit(boolean safe)方法来实现,只是传的参数不一样而已。这个在MessageQueue那一小节再来分析。

3.2 Message类

Message类用来保存消息的内容。我们先来看下Message类会保存哪些消息:

3.2.1 Message类主要成员变量

成员变量 类型 含义
what int 消息类型
obj Object 消息内容
when long 消息触发时间
target Handler 消息处理者
callback Runnable 回调方法
sPool Message 消息池
sPoolSize int 消息池大小
next Message 下一条消息

这里只列举了一部分,详细的可以去看Message类的源码。

3.2.2 获取消息

Message内部维护了一个消息池,我们可以通过obtain()来从消息池中获取消息,而不是直接去new,这样可以提高效率。

3.2.2.1 Message的obtain()
    public static Message obtain() {
        synchronized (sPoolSync) {
            //如果消息池不为null,则从消息池中取出一条消息
            if (sPool != null) {
                //从sPool中取出头结点
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // 清除in-use标记
                sPoolSize--;
                return m;
            }
        }
        //如果消息池为null,则直接new
        return new Message();
    }

3.2.3 回收消息

3.2.3.1 Message的recycle()
    public void recycle() {
        //判断消息是否正在使用
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        //调用recycleUnchecked()
        recycleUnchecked();
    }

调用recycleUnchecked()来回收。

3.2.3.2 Message的recycleUnchecked()
    void recycleUnchecked() {
        //将消息标记为使用状态
        //清空消息其他参数
        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) {
            //如果没有达到消息池的最大容量,则将消息回收到消息池中去
            //最大容量默认为50
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

3.2.4 小结

Message内部维护了一个消息池,这个消息池是以链表的形式存在的。通过消息池去获取Message对象能够避免直接创建对象,可以起到一个提高效率的作用。

3.3 Handler类

通常,我们都会通过继承Handler类来自定义一个Handler子类,然后重写handleMessage()方法来处理消息。同时,也是通过这个Handler子类来进行发送消息的。

我们先来看下Handler的构造方法。

3.3.1 Handler的构造方法

3.3.1.1 Handler的无参构造方法
    public Handler() {
        this(null, false);
    }

最终会调用有参数的构造方法Handler(Callback callback, boolean async)

3.3.1.2 Handler的有参构造方法

先来看下上面提到的Handler(Callback callback, boolean async)

    public Handler(Callback callback, boolean async) {
        //匿名类、内部类或本地类应该申明为static,否则会警告可能出现内存泄露
        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());
            }
        }

        //获得当前线程的Looper对象
        //myLooper()实际上通过sThreadLocal.get()来获取的
        mLooper = Looper.myLooper();
        //如果mLooper为null则抛出异常
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        
        //获得Looper对象中的消息队列
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

下面列出所有Handler的所有有参构造方法

    public Handler(Callback callback);
    public Handler(Looper looper);
    public Handler(Looper looper, Callback callback);
    public Handler(boolean async);
    public Handler(Callback callback, boolean async);
    public Handler(Looper looper, Callback callback, boolean async);

可以看到:除了callbackasync之外,还可以传递一个Looper进来,即可以指定跟Handler要绑定的Looper,相关代码就不贴了,还是很简单的。

3.3.2 Handler发送消息

通常我们都是通过HandlersendMessage()方法来发送消息的:

3.3.2.1 Handler的sendMessage()
    public final boolean sendMessage(Message msg)
    {   
        //调用sendMessageDelayed()
        return sendMessageDelayed(msg, 0);
    }
3.3.2.2 Handler的sendMessageDelayed()
   public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        //调用sendMessageDelayed()
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
3.3.2.3 Handler的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;
        }
        //调用enqueueMessage(),消息入队
        return enqueueMessage(queue, msg, uptimeMillis);
    }
3.3.2.4 Handler的enqueueMessage()
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //把当前的Handler对象保存到消息中的target中去
        //这样消息分发时才能找到相应的Handler去处理
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //把消息放到消息队列中去
        return queue.enqueueMessage(msg, uptimeMillis);
    }

最终,发送消息就是将消息放到消息队列中去。

3.3.3 Handler发送Runnable

Handler除了sendMessage(Message msg)外,还可以发送一个Runnable出来,这是通过其post()方法实现的:

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

post()方法里面同样也是通过sendMessageDelayed()来发送消息,我们来看下getPostMessage(r)这个方法:

3.3.3.1 Handler的getPostMessage()
    private static Message getPostMessage(Runnable r) {
        //获取消息
        Message m = Message.obtain();
        //Runnable赋值给Message中的callback
        m.callback = r;
        //返回一个Message
        return m;
    }

所以,post()最终还是将Runnable对象包装成一个Message来进行消息发送的。

3.3.4 分发消息

前面Looper在消息队列中取到消息后就调用msg.target.dispatchMessage(msg);来分发消息,这里的msg.target就是Handler。我们来看下dispatchMessage()方法:

3.3.4.1 Handler的dispatchMessagee()
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            //当msg.callback不为null时,会回调message.callback.run()方法
            //即执行Runnable的run()方法
            handleCallback(msg);
        } else {
            //当Handler存在Callback时,回调Callback的handleMessage();
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //调用Handler的handleMessage(msg)
            //即我们继承Handler重写的handleMessage(msg)方法
            handleMessage(msg);
        }
    }

我们再来看下handleCallback()这个方法:

3.3.4.2 Handler的handleCallback()
    private static void handleCallback(Message message) {
        //执行Runnable的run()方法
        message.callback.run();
    }
3.3.4.3 小结

从上面的代码可以看到,分发消息的流程如下:

  1. 如果msg.callback不为null,这个msg.callback实际是个Runnable对象,则调用这个Runnablerun()方法,结束;为null的话就走到步骤2。
  2. 如果Handler的成员变量mCallback不为null,则调用mCallback.handleMessage(msg),结束;为null的话就走到步骤3。
  3. 调用HandlerhandleMessage(msg)方法,结束。

3.4 MessageQueue类

我们再来看下MessageQueue类,主要包括消息入队、取出消息和退出等。

3.4.1 MessageQueue构造方法

    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        //通过native方法去初始化化消息队列
        mPtr = nativeInit();
    }

3.4.2 消息入队

3.4.2.1 MessageQueue的enqueueMessage()
    boolean enqueueMessage(Message msg, long when) {
        //判断消息是否关联了Handler,若无则抛异常
        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();
            //获取message的when时间(触发时间)
            msg.when = when;
            //获取消息队列里的消息
            //Message是个链表结构
            Message p = mMessages;
            boolean needWake;
            //如果消息队列里没有消息
            //或者发生时间(when)在链表的头结点之前
            if (p == null || when == 0 || when < p.when) {
                //将消息插入到链表的头结点,即放入队头
                msg.next = p;
                mMessages = msg;
                //如果处于阻塞状态,则唤醒
                needWake = mBlocked;
            } else {
                //如果消息队列中已存在消息且触发时间(when)在链表的头结点之后
                //则插入到队列中间
                
                //通常这里的needWake为false,即不需唤醒消息队列
                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;
            }

            if (needWake) {//如果需要唤醒
                //在native层唤醒消息队列
                nativeWake(mPtr);
            }
        }
        return true;
    }

可以看到,消息队列是根据消息触发时间来进行排队的,触发时间最早的消息将会排到队列的头部。当有新消息需要加入消息队列时,会从队列头开始遍历,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序。
如果消息队列需要唤醒,则会在消息加入消息队列后对消息队列进行唤醒。

3.4.3 取出消息

3.4.3.1 MessageQueue的next()
    Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {
            //当消息循环已经退出,直接返回
            return null;
        }
        //只有首次迭代为-1
        int pendingIdleHandlerCount = -1; 
        int nextPollTimeoutMillis = 0;
        //死循环
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //这里会阻塞,直到nextPollTimeoutMillis超时或者消息队列被唤醒
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    //当target为空时,在消息队列中循环查找到下一条异步消息
                    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;
                }

                //如果消息正在退出,返回null
                if (mQuitting) {
                    dispose();
                    return null;
                }
                //如果当前是第一次循环时
                //且当前消息队列为空时,或者下一条消息还没准备好时
                //即当前处于空闲的状态
                //那么就获取Idle Handler的数量
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    //没有idle handlers 需要运行,继续循环并等待。
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            //运行idle handlers
            //只有第一次循环时才会执行这些代码块
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                //释放handler的引用
                mPendingIdleHandlers[i] = null; 

                boolean keep = false;
                try {
                    //执行idler的queueIdle()
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        //根据idler.queueIdle()的返回值来判断是否移除idler
                        //即返回true的话能够重复执行
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            //重置IdleHandler的数量为0,这样就保证不会重复运行
            //即只有第一次循环时会运行
            pendingIdleHandlerCount = 0;

            //重置超时时间为0
            //即当调用一个idle handler时, 一个新的消息能够被分发,因此无需等待即可回去继续查找还未被处理的消息
            nextPollTimeoutMillis = 0;
        }
    }

取出消息时,如果没有消息或者超时时间还没到,则会处于阻塞的状态,直到超时时间过去或者消息队列被唤醒。当消息准备好时,才会返回消息出去。
另外,如果当前处于空闲的状态,则会执行IdleHandler中的方法。

3.4.4 消息队列退出

3.4.4.1 MessageQueue的quit()
    void quit(boolean safe) {
        //主线程的消息队列是不允许退出的,主要还是看mQuitAllowed的值
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            //如果正在退出,则直接返回,防止多次操作
            if (mQuitting) {
                return;
            }
            //设置为正在退出中
            mQuitting = true;
            //判断是否安全移除
            if (safe) {
                //移除尚未触发的所有消息
                removeAllFutureMessagesLocked();
            } else {
                //移除所有的消息
                removeAllMessagesLocked();
            }

            nativeWake(mPtr);
        }
    }

如果是安全退出,那么只会移除尚未触发的所有消息,对于正在触发的消息并不移除;
如果不是安全退出,则直接移除所有的消息。

4.一些面试题

这里收集了一些常见的面试题来解答一下,大部分答案其实都能在上面的分析中找到的,所以嘛,要认真看代码。
这里只列出一部分题目,如果有好的题目也可以留言补充哈~

4.1 Q:Looper.loop()是个死循环,主线程为什么不会卡死?

对于一个线程,如果执行完代码之后就会正常退出。但是对于主线程,我们肯定是希望能够一直存活的,那么最简单的方法就是写个死循环让它一直在执行,这样就不会退出了。那么主线程为什么不会卡死呢?如果消息队列里面有消息,Looper就取出来出来;如果没有消息就会阻塞,直到有新消息进来唤醒消息队列去处理,这一过程就是在这个死循环中处理的,所以说Looper本身是不会卡死的。像ActivityonCreate()onResume()等生命周期实际上就是在这个死循环中执行的。如果我们在ActivityonCreate()onResume()中做一些耗时操作,可能就会发生掉帧,甚至出现ANR,这才是我们看到的卡顿卡死现象。

4.2 Q:一个线程中是否可以有多个Handler?如果有多个,分发消息是如何区分的?

是可以有多个的。我们使用Handler发送Message时,Message中的target变量会保存当前的Handler引用,分发消息时就是靠这个target来区分不同的Handler

有问题请留言补充吧~~

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

推荐阅读更多精彩内容