Handler机制源码探究

247 阅读18分钟

前言

  • Handler是一套Android消息传递机制

  • 在Android开发的多线程应用场景中,Handler机制十分常用

  • 在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现 工作线程对UI的更新处理,最终实现异步消息的处理。

一、Handler机制结构

1、Handler基本机构

 
  • 处理器Handler

     发送和接收消息,并处理消息

  • 消息队列

     消息队列,用于存储消息体,是一个队列

  • 循环器

     用于轮询消息队列,一个线程只能有一个Looper

  • 消息体

     用来传递数据的消息,带有需要传递的信息

二、Hanlder类解析

1、构造函数

handler主要有七个构造函数;主要参数有三个,分别是:

  • Callback:回调接口

  • Looper:循环器,主要是针对非UI线程,需要创建循环器

  • async:boolean true为异步,false为同步

构造函数一:

 public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
 }

参数全部由外部传入。

构造函数二:

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

对当前线程创建looper;

2、创建消息

handler创建消息体,底层依旧是调用的Message.obtain(this)方法;但是handler类对该方法进行了大量封装,以便于快速创建消息。

// 带有msg.what的方法
public final Message obtainMessage(int what){
        return Message.obtain(this, what);
}
// 带有msg.obj和msg.what的方法
public final Message obtainMessage(int what, Object obj){
    return Message.obtain(this, what, obj);
}

3、发送消息

handler发送消息有以下两个系列的方法,分别是send(Message)系列和post(Runnable)系列;

  • sendMessage系列

  • 包含有sendMessage(Message msg)、sendEmptyMessage(int what)、sendEmptyMessageDelayed(int what, long delayMillis)、sendEmptyMessageAtTime(int what, long uptimeMillis) 、sendMessageDelayed(Message msg, long delayMillis)、sendMessageAtTime(Message msg, long uptimeMillis)、sendMessageAtFrontOfQueue(Message msg)

例如:

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
}
public final boolean sendMessageAtFrontOfQueue(Message msg) {
        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, 0);
}
  • post(Runnable)系列

  • 主要包含post(Runnable r)、postAtTime(Runnable r, long uptimeMillis)、postAtTime(Runnable r, Object token, long uptimeMillis)、postDelayed(Runnable r, long delayMillis)、postAtFrontOfQueue(Runnable r)

  • post系列的底层,一样是使用的send系列方法;

public final boolean post(Runnable r){
       return  sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis){
       return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtFrontOfQueue(Runnable r){
        return sendMessageAtFrontOfQueue(getPostMessage(r));
}
  • 发送消息终极方法

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

上面所有的post系列,send系列方法最后都会调用这个方法:enqueueMessage; 最后调用queue的enqueueMessage方法把msg入队,对这个方法不了解的,可以看前边的MessageQueue源码解析。

SystemClock.uptimeMillis(),就是表示系统开机到当前的时间总数,如果有延迟,就加上延迟时间,分析到现在,我们也能发现,postDelayed不是延迟多少秒发送消息,这个消息是直接发送给队列的,不过在MessaegQueue中,消息按时间排放的,不到时间不会把它取出来,所以应该说延迟多少秒取出消息更合适。

4、处理消息

 在handleMessage(Message)方法中,我们可以拿到message对象,根据不同的需求进行处理;这是一个空方法,需要使用者具体实现。
public void handleMessage(Message msg) {}

在dispatchMessage(Message msg)方法中,如果msg自己设置了回调方法,就需要dispatchMessage来处理;

// 处理消息
public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
}
// 执行消息的callback
private static void handleCallback(Message message) {
        message.callback.run();
}

5、删除消息

 removeCallbacks(Runnable r)方法,就是移出messageQueue中所有满足条件的message,前提是消息还没Looper取走。
public final void removeCallbacks(Runnable r){
      mQueue.removeMessages(this, r, null);
 }
  
public final void removeCallbacks(Runnable r, Object token){
      mQueue.removeMessages(this, r, token);
 }
 

三、MessageQueue类机制

首先确认一点,MessageQueue并不是一个标准的queue,底层数据结构是单向链表;并不遵循queue的先进先出原则。

 MessageQueue中最重要的就是两个方法:
  • enqueueMessage向队列中插入消息

  • next 从队列中取出消息

  1. 变量

  • mMessages表示消息队列的头Head;

  • mIdleHandlers是IdldHandler接口的ArrayList, mPendingIdleHandlers是数组版本,在后面的代码中会将ArrayList的内容拷贝到它里面;

  • mQuitting表示当前队列是否处于正在退出状态;

  • mBlocked表示next()调用是否被block在timeout不为0的pollOnce上;

  • mNextBarrierToken表示下一个barrier token,barrier用target==null, arg1==token的Message对象表示;

  1. 构造函数

    mQuitAllowed表示MessageQueue是否允许退出,系统创建的UI线程的MessageQueue是不允许的;其他线程代码创建的都是允许的;

    mPtr是native代码相关的,指向C/C++代码中的某些对象(指针),其他一些nativeXXX()相关的函数本文暂不做分析

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();
}

MessageQueue的构造方法简单的调用了nativeInit()来进行初始化,这是一个jni方法,也就是说,可能是在JNI层维持了它这个消息队列的对象。在message中有好多native方法,可以看出message是比较底层的一个类。

  1. enqueueMessage(消息入列)

先上源码:

boolean enqueueMessage(Message msg, long when) {
        if (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;//将延迟时间封装到msg内部
            Message p = mMessages;//消息队列的第一个元素
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
               //如果此队列中头部元素是null(空的队列,一般是第一次),或者此消息不是延时的消息,则此消息需要被立即处理,此时会将这个消息作为新的头部元素,并将此消息的next指向旧的头部元素,然后判断如果Looper获取消息的线程如果是阻塞状态则唤醒它,让它立刻去拿消息处理
          
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                //如果此消息是延时的消息,则将其添加到队列中,原理就是链表的添加新元素,按照when,也就是延迟的时间来插入的,延迟的时间越长,越靠后,这样就得到一条有序的延时消息链表,取出消息的时候,延迟时间越小的,就被先获取了。插入延时消息不需要唤醒Looper线程。
  
                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;
    }

和任何方法一样,在enqueue之前,也都是对参数msg的检查,比如msg如果在使用中,或者msg的target是null,都会抛出AndroidRuntimeException,进行完条件检查后,会进入真正的处理逻辑。接下来的操作类似在一张单链表中插入一个元素:进入同步块

  1. 如果此队列处于正在退出的状态则不能在往里入队了,不能插入元素了,在这种情况下会抛出RuntimeException,然后return false,表示失败了;

  2. 接下来表示队列的状态ok,设置msg的when字段,临时变量p指向队列头;(必要的初始化,准备工作)

  3. 如果队列是空的或when==0或when<p.when,也就是说要插入的这个message应该在第一个位置也就是队首,那么它将是新的Head,将它和原先的队列连接起来;

  4. 否则插入将发生在队列中间的某个位置(有可能是队尾),将msg插在第一个p的前面,p满足这个条件(p == null || when < p.when)。最后退出同步块,返回true,表示操作(入队)成功。

注意:MessageQueue中的元素是按序按时间先后插入的(先执行的在前)

  1. next()

Message next() {
    
        final long ptr = mPtr;
        if (ptr == 0) {
           //从注释可以看出,只有looper被放弃的时候(调用了quit方法)才返回null,mPtr是MessageQueue的一个long型成员变量,关联的是一个在C++层的MessageQueue,阻塞操作就是通过底层的这个MessageQueue来操作的;当队列被放弃的时候其变为0。
            return null;
        }
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的。
           //如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
           //如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
           //如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
           
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    //msg.target == null表示此消息为消息屏障(通过postSyncBarrier方法发送来的)
                    //如果发现了一个消息屏障,会循环找出第一个异步消息(如果有异步消息的话),所有同步消息都将忽略(平常发送的一般都是同步消息)
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // 如果消息此刻还没有到时间,设置一下阻塞时间nextPollTimeoutMillis,进入下次循环的时候会调用nativePollOnce(ptr, nextPollTimeoutMillis)进行阻塞;
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //正常取出消息
                        //设置mBlocked = false代表目前没有阻塞
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    //没有消息,会一直阻塞,直到被唤醒
                    nextPollTimeoutMillis = -1;
                }
                if (mQuitting) {
                    dispose();
                    return null;
                }
            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }
  1. 当首次进入或所有消息队列已经处理完成,由于此刻队列中没有消息(mMessages为null),这时nextPollTimeoutMillis = -1 ,然后会处理一些不紧急的任务(IdleHandler),之后线程会一直阻塞,直到被主动唤醒(插入消息后根据消息类型决定是否需要唤醒)。

  2. 读取列表中的消息,如果发现消息屏障,则跳过后面的同步消息。

  3. 如果拿到的消息还没有到时间,则重新赋值nextPollTimeoutMillis = 延时的时间,线程会阻塞,直到时间到后自动唤醒。

  4. 如果消息是及时消息或延时消息的时间到了,则会返回此消息给looper处理。

注意:Next方法取出下一个Message(从头部取),如果没有Message可以处理,就可以处理下IdleHandler。

  1. removeMessages

从队列中删除所有匹配的元素。总体思想都是先从队首删除,如果删除了,则队首

指向接下来的元素,重复这个过程,直到第一个不匹配的元素出现。接着从这个元素之后(after front)开始查找并删除,方法是链表删除后一个节点的方法,即p.next=nn。注意这里都是删除所有匹配的消息,而不是第一个匹配的。

  1. IdleHandler

IdleHandler 可以用来提升性能,主要用在我们希望能够在当前线程消息队列空闲时做些事情(譬如 UI 线程在显示完成后,如果线程空闲我们就可以提前准备其他内容)的情况下,不过最好不要做耗时操作。

//getMainLooper().myQueue()或者Looper.myQueue()
Looper.myQueue().addIdleHandler(new IdleHandler() {  
    @Override  
    public boolean queueIdle() {  
        //你要处理的事情
        return false;    
    }  
});

四、Looper类机制

Looper对象,顾名思义,直译过来就是循环的意思,从MessageQueue中不断取出message。

  1. 构造函数

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

初始化了messageQueue并绑定当前线程。

  1. Looper.prepare

public static void prepare() {
        prepare(true);
}
 
private static void prepare(boolean quitAllowed) {
     if (sThreadLocal.get() != null) {//每个线程只能有一个looper
            throw new RuntimeException("Only one Looper may be created per thread");
        }
      //设置本线程的looper。
      sThreadLocal.set(new Looper(quitAllowed));
  }

注意prepare(boolean)方法中,有一个sThreadLocal变量(ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本),这个变量有点像一个哈希表,它的key是当前的线程,也就是说,它可以存储一些数据/引用,这些数据/引用是与当前线程是一一对应的,在这里的作用是,它判断一下当前线程是否有Looper这个对象,如果有,那么就报错了,"Only one Looper may be created per thread",一个线程只允许创建一个Looper,如果没有,就new一个。然后它调用了Looper的构造方法。

3.Looper.loop

public static void loop() {
        final Looper me = myLooper();//返回当前的looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;//取得messageQueue
         
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            。。。。
            msg.target.dispatchMessage(msg);
            。。。
            msg.recycleUnchecked();
        }
    }
  • 首先调用了静态方法myLooper()获取当前Looper对象。没有looper则抛出异常。

  • myLooper()同样是静态方法,它是直接从这个ThreadLocal中去获取Looper对象,拿到当前线程的Looper后;

  • MessageQueue queue = me.mQueue;拿到与这个Looper对应的MQ,就开始无限循环,不断的从消息队列中取出Message,当获取到一个消息后,它调用了Message.target.dispatchMessage()方法来对消息进行处理。dispatchMessage这个方法在Handler中,就是处理message。

  • 3.msg.recycleUnchecked(),当这个msg处理完了,就没有用啦,把它回收到全局池中,注意不是销毁。

4、总结

Looper的代码看完了,我们得到了几个信息:

  • Looper调用静态方法prepare()来进行初始化,一个线程只能创建一个与之对应的Looper,Looper初始化的时候会创建一个MQ,并和当前线程绑定,因此,有了这样的对应关系,一个线程对应一个Looper,一个Looper对应一个MQ。

  • Looper调用静态方法loop()开始无限循环的取消息,messageQueue调用next()方法来获取消息(这也是个无限循环)。

五、Message类机制

Message类是个final类,就是说不能被继承,同时Message类实现了Parcelable接口,我们知道android提供了一种新的类型:Parcel。本类被用作封装数据的容器,是链表结构,有两个属性next和sPool,这两个变量是不同的。

  定义一个包含任意类型的描述数据对象,此对象可以发送给Handler。对象包含两个额外的int字段和一个额外的对象字段,这样可以使得在很多情况下不用做分配工作。尽管Message的构造器是公开的,但是获取Message对象的最好方法是调用Message.obtain()或者Handler.obtainMessage(), 这样是从一个可回收对象池中获取Message对象。
  1. 变量

  • what:用户定义消息代码以便收件人可以识别这是哪一个Message。每个Handler用它自己的名称空间为消息代码,所以您不需要担心你的Handler与其他handler冲突。

  • arg1、arg2:如果只是想向message内放一些整数值,可以使用arg1和arg2来代替setData方法。

  • obj:发送给接收器的任意对象。当使用Message对象在线程间传递消息时,如果它包含一个Parcelable的结构类(不是由应用程序实现的类),此字段必须为非空(non-null)。其他的数据传输则使用setData(Bundle)方法。注意Parcelable对象是从FROYO版本以后才开始支持的。

  • replyTo:指明此message发送到何处的可选Messenger对象。具体的使用方法由发送者和接受者决定。

  • FLAG_IN_USE:判断Message是否在使用( default 包内可见)

  • FLAG_ASYNCHRONOUS:如果设置message是异步的。

  • FLAGS_TO_CLEAR_ON_COPY_FROM:明确在copyFrom方法

  1. Obtain方法

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();
}
  • 先是一个对象的同步锁,如果sPools不为空的话,把sPools赋值给Message,并把Message的next赋值给sPool,当前Message的next为空null,flags标记清零,sPoolSize减减,返回我们需要的Message;

  • 如果sPools为空的话直接new一个Message;

但看到这里还是很模糊,虽然sPool看上去像一个消息池,但再仔细看居然是一个Message对象,这样真的就能避免多次构建Message对象吗?继续看会发现一个next字段,再看它的注释

sometimes we store linked lists of these things
,Message的消息池原来是一个链表,如下图所示

每一个Message 对象通过next指向下一个Message(最后一个Message的next为null)形成一个链表,Message对象就成了一个可用的Message池,所以,Message对象原来是从链表中获取的。

  1. recycle()方法

public void recycle() {
        //判断消息是否还在用
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();
}
 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++;
            }
        }
 }
  • 在recycleUnchecked函数中会先清空该消息的各个字段,并且把flags设置为FLGA_IN_USE,表明该消息已经被使用了。然后判断是否要将消息回收到消息池中,如果池的大小小于MAX_POOL_SIZE,就将自身添加到链表的表头,sPoolSize++。

  • 例如最开始的开始的时候链表中没有任何消息,将第一个Message对象添加到表中,此时的sPool为空,因此next也为空,sPool又指向this,这时sPool就指向当前这个被回收的Message对象,sPoolSize加1。我们把这个Message命名为m1,这时的链表应该如下:

图片

  • 如果再次插入一个名为m2的Message,那么m2将被插入表头,sPool指向m2,这时sPool的链表中结构如下:

图片

对象池默认的大小为50,如果池的大小小于50,被回收的消息将会被插入到链表头部。

调用obtain()方法时,流程如下:

如果池中有元素,这时候再调用obtain函数时,实际上是就获取链表中表头的元素,也就是sPool。再把sPool指针往后移动一个。

在obtain函数中,首先会声明一个Message对象m,并且让m指向sPool.sPool实际上指向了m2,因此m实际上指向的也是m2,这里相当于保持了m2这个元素。

下一步是sPool指向m2的下一个元素,也就是m1。sPool也完成后移之后此时把m.next置空,也就相当于m2.next变成了null。

最后就是m指向了m2元素,m2的next为空,sPool从原来的表头m2指向了下一个元素m1,最后将对象的元素减1,这样m2就顺利的脱离了消息池队伍,就返回给了调用obtain函数的。

图片

六、QA

  1. Handler.sendMessageDelayed()怎么实现延迟的?

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
  • 可以看出 : 消息被处理的时间 = 当前时间+延迟的时间;

  • 前面我们分析了如果拿到的Message还没有到执行时间,则会重新设置超时时间并赋值给nextPollTimeoutMillis,然后调用nativePollOnce(ptr, nextPollTimeoutMillis)进行阻塞;nativePollOnce()的作用类似与object.wait()。

  1. 如果Message会阻塞MessageQueue的话,那么先postDelay10秒一个Runnable A,消息队列会一直阻塞,然后我再post一个Runnable B,是否会执行?

这个问题的关键在于needWake,在next()方法内部,如果有阻塞(没有消息了或者只有Delay的消息),会把mBlocked这个变量标记为true,在下一个Message进队时会判断这个message的位置,如果在队首就会调用nativeWake()方法唤醒线程!

  1. postDelay()一个10秒钟的Runnable A、消息进队,MessageQueue调用nativePollOnce()阻塞,Looper阻塞;

  2. 紧接着post()一个Runnable B、消息进队,判断现在A时间还没到、正在阻塞,把B插入消息队列的头部(A的前面),然后调用nativeWake()方法唤醒线程;

  3. MessageQueue.next()方法被唤醒后,重新开始读取消息链表,第一个消息B无延时,直接返回给Looper;

  4. Looper处理完这个消息再次调用next()方法,MessageQueue继续读取消息链表,第二个消息A还没到时间,计算一下剩余时间(假如还剩9秒)继续调用nativePollOnce()阻塞;

  5. 直到阻塞时间到或者下一次有Message进队; ###

  6. Looper.loop是一个死循环,拿不到需要处理的Message就会阻塞,那在UI线程中为什么不会导致ANR?

首先我们来看造成ANR的原因:

  • 当前的事件没有机会得到处理(即主线程正在处理前一个事件,没有及时的完成或者looper被某种原因阻塞住了)。

  • 当前的事件正在处理,但没有及时完成。

我们再来看一下APP的入口ActivityThread的main方法:

public static void main(String[] args) {
  
        ...
        Looper.prepareMainLooper();
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

显而易见的,如果main方法中没有looper进行死循环,那么主线程一运行完毕就会退出,会导致直接崩溃,程序结束! 现在我们知道了消息循环的必要性,那为什么这个死循环不会造成ANR异常呢?

我们知道Android 的是由事件驱动的,looper.loop() 不断地接收事件、处理事件,每一个点击触摸或者说Activity的生命周期都是
运行在 Looper的控制之下,如果它停止了,应用也就停止了。只能是某一个消息或者说对消息的处理阻塞了 Looper.loop(),而不是
Looper.loop() 阻塞它,这也就是我们为什么不能在UI线程中处理耗时操作的原因。 主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,唤醒主线程,主线程被唤醒只是
为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。