Handler机制浅析

本文我们主要是要介绍Handler机制,但是涉及到Handler又不得不介绍Message,MessageQueue,Looper,Handler机制主要是依赖后面几个的,所以我们在文中会一次介绍到他们几个。

通过本文你可能会了解到一下几点

  • 1.Handler机制及Handler与Message,MessageQueue,Looper的关系
  • 2.Handler在子线程中的应用及原理
  • 3.Message的复用机制
  • 4.为什么主线程可以有死循环(loop())

Handler源码位置:

android\frameworks\base\core\java\android\os\Handler.java

首先看他的构造:

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

    public Handler(Callback callback) {
        this(callback, false);
    }

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

    public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }

    public Handler(boolean async) {
        this(null, async);
    }

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

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

一般我们都用的是无参的构造。先说一下那个Callback,他是一个接口:

    public interface Callback {
        public boolean handleMessage(Message msg);
    }

这是处理消息的一种手段,一般我们也很少用,因为我们都重写了handleMessage方法,效果一样的,我们主要从无参构造这条线看下去。

在无参构造中,直接调用了参数类型为Callback 和boolean 的构造。这里面他给Looper赋值为Looper.myLooper()。来看一下这个方法:

Looper源码位置:

android\frameworks\base\core\java\android\os\Looper.java
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

这个方法实际上就是获取当前线程的looper,ThreadLocal是Java的lang包中的类,他的set方法是在当前线程中保存一个对象,get是从当前线程中取出那个对象。这里存取的就是Looper。但这个Looper在哪存的呢?还是从源码中找答案:

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

有些人看到这应该就明白了,这里也给出了另一个问题的答案,为什么在子线程中使用Handler或者Toast时要先调用Looper.prepare()。原来这个方法有一个重要的作用就是给当前线程设置Looper,如果不设置,就是null,当然会有问题。但为什么主线程中不需要呢?

这个问题要从app的创建说起,但具体的app启动流程这里就不详细叙述了,只说明一点,在启动app的进程时,Zygote会通过socket接受AMS的请求,通过反射的方法调用ActivityThread的main方法,ActivityThread也就是一个应用的线程,也就是主线程,我们看一下这个方法:

    public static void main(String[] args) {
        ....

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        ....
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

这里调用了prepareMainLooper方法,它和prepare有什么区别呢?直接看源码:

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

可见prepareMainLooper也是调用了prepare,只有参数的差别,一个为false一个为true。这个参数最后用到了Looper的构造上:

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

在构造上又传给了MessageQueue。这个参数quitAllowed的作用在MessageQueue中很清楚:

android\frameworks\base\core\java\android\os\MessageQueue.java
    void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        ....
    }

只在这里用到了上文中的参数quitAllowed,也就是为true时,会报异常,异常也很清楚,就是主线程不允许调用quit退出,这个值只在调用prepareMainLooper方法时为true,我们用的时候都是调用prepare方法,也就是为false。

这里做一个小结,Looper.prepare()最主要的作用是初始化Looper,而Handler是基于Looper,一个线程创建后是没有Looper,主线程也不例外,要想使用Handler就必须初始化,一般线程调用prepare方法,主线程调用prepareMainLooper。

下面我们还回到构造方法中去。

这里判断了一下mLooper 是否为空,若为空抛异常,这也就是子线程中不能直接使用的直接来源。然后初始化了MessageQueue类型成员变量mQueue ,他从Looper中获得,Looper中是在构造函数中初始化的。又初始化了成员变量mCallback ,当然我们这条线上他为空。最后初始化了标志位mAsynchronous ,我们这里为false,这个标志位的左后后面会提到。

到这里Handler的构造就完成了,除了初始化一些东西也没做什么。我们一般用的时候在构造完之后,就是重写handleMessage方法,用于处理接受到的消息。下面分析从发送消息到接受消息的流程。

先从最普通的sendMessage开始:

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

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

发现sendMessage内部调用的是sendMessageDelayed,也就是延迟为0,很好理解。主要看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;
        }
        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);
    }

这个里面先获取了消息队列,这个变量在构造中初始化,就是Looper中的队列,在looper中的构造中初始化。接下来调用了enqueueMessage方法,这个方法里,首先设置Message 的target 为当前Handler,方便调用sendToTarget时使用。最后调用了MessageQueue 的enqueueMessage方法入队。我们看一下这个方法,还是很有意思的:

    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) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                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; 
                prev.next = msg;
            }

            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

这里就需要有一些数据结构的功底了。这个方法的主要思想是利用next 成员创建一个链表。方法中首先判断了target 是否为空及此msg是否使用,然后判断了mQuitting变量,这个变量在quit方法中为true,在主线程中这个变量永远为false。接下来标记该msg已用,然后配置该msg的执行时间。

下面就是链表的操作了,如果链表为空,或者该msg需要立即执行或者,该msg执行时间小于第一位的msg,就把他插入到链表头部(链表插入方法中的头插法,代码看不懂的可以画图手动操作一些,会有奇效)。

如果不满足上面三个条件,就循环遍历所有链表结点,根据时间插入到合适位置,这里涉及到链表的插入操作,代码看不懂的画图演示比较直观有效。其中有一点很重要,这个链表是有序的。

消息进入队列后,发消息的第一步就是要先从队列中取消息,我们来看这一步,这一步在Looper中的loop方法,还记得如何在子线程中使用Handler么,执行完prepare方法后,就可以设置Handler了,但此时Handler还没有用,缺少一个驱动,这个驱动就是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;

        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); 
            if (msg == null) {
                return;
            }

            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

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

这里首先也验证了一下,保证Looper 不为空。然后获取了MessageQueue 。接下来开启了一个死循环(看到这,有人也许会问,在ActivityThread中也调用了该方法,为什么主线程中有个死循环,而且还没问题,这个问题最后在讲),这个循环的目的就是不断地从队列中取消息,取消息用的是queue.next(),这是一个阻塞方法(这个过程类似于Socket中服务端监听请求的过程),我们来看next方法:

    Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1;
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

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

                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    mBlocked = true;
                    continue;
                }

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

            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

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

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }


            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }

先看第一个判断,这里出现一个变量mPtr,这个变量是什么哪里来的?他是在构造中初始化的,调用了native方法nativeInit();既然是源码分析,所以虽然是jni我们也要去看看。源码位置:

android\frameworks\base\core\jni\android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
    if (!nativeMessageQueue) {
        jniThrowRuntimeException(env, "Unable to allocate native queue");
        return 0;
    }

    nativeMessageQueue->incStrong(env);
    return reinterpret_cast<jlong>(nativeMessageQueue);
}

这里创建了native层的MessageQueue对象,这个类定义就在android_os_MessageQueue.cpp中。nativeInit的返回值只是为了标志NativeMessageQueue是否创建成功,不成功的会返回0,成功的返回指针地址(转为long类型),另外在MessageQueue 的dispose()方法中会把他置为0。我们主要还是回到next方法中,这个标志位主要标志该队列是否可用,不可用返回null即可。下面才是正式的取消息部分。

这里你会惊奇的发现有一个死循环,这也就是为什么这个方法时阻塞的原因。看来到了较底层的部分,也没有什么花哨技巧了,直接用上死循环来处理。我们直接看synchronized 代码块的第一个判断,里面有一个target == null的判断,我们在enqueueMessage知道,如果target 为null的话会直接抛异常,那么会什么会有target 为空的msg呢?实际上MessageQueue 有这样一个方法:

   public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { 
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

这个方法也是插入一个msg,但是是target为空的特殊msg,称之为SyncBarrier。这种msg的作用就是拦截,所有执行时间在这个msg之后的同步消息都不会执行,直到遇到下一个SyncBarrier,也就是异步和同步之分。如果你记忆力够好或者没有被源码绕晕的话,希望你记得在Handler构造中初始化了一个参数mAsynchronous ,若从空参来的话,他为false。在Handler的enqueueMessage中发挥作用,若为true的话,所有msg会setAsynchronous(true)。一直影响到next方法里,若isAsynchronous()为true,会不受异步影响,若isAsynchronous()为false,会受异步影响,跳过所有同步消息。好吧,跨度有点大,多回头看看源码捋一捋就好。

我们从正常情况开始分析,就是isAsynchronous()为false,但是没有插入SyncBarrier。这时取到头部msg,判断是否为null,不为空的话,检测执行时间,如果未到时间,则设置nextPollTimeoutMillis 为等待时间,若到时间,则从队列移除该msg,重设头结点,并返回该消息。这时正常取到msg的流程,若msg==null,设置nextPollTimeoutMillis =-1,然后判断是不是因为调用quit导致的(调用该方法会清空队列),若是则返回null。最后看一下是否有IdleHandlers,IdleHandler是是一种利用系统空闲时机去处理一些不重要事件用的,如gc,这和我们要讲的消息关系不大。且看逻辑,如果没有要执行的IdleHandlers,则阻塞设为true,继续循环(此时意味着取到了一个取到了一个消息,但还没到执行事件,nextPollTimeoutMillis为大于零某数,或没有取到消息,nextPollTimeoutMillis为-1),下一次循环,会到nativePollOnce方法,我们这里介绍一下这个方法:

static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jlong ptr, jint timeoutMillis) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
    mPollEnv = env;
    mPollObj = pollObj;
    mLooper->pollOnce(timeoutMillis);
    mPollObj = NULL;
    mPollEnv = NULL;

    if (mExceptionObj) {
        env->Throw(mExceptionObj);
        env->DeleteLocalRef(mExceptionObj);
        mExceptionObj = NULL;
    }
}

他先还原了之前保存的NativeMessageQueue对象,然后执行了mLooper->pollOnce(timeoutMillis)。mLooper是native层的Looper对象,在NativeMessageQueue构造中初始化,他和java的Looper并没多大关系:

NativeMessageQueue::NativeMessageQueue() :
        mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}

Looper.cpp位置:

android\system\core\libutils\Looper.cpp

pollOnce内部又调用了pollInner方法,这个方法非常长,这里就不贴出来了,关键一步在:

int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

到这里已经超出Android编程范围了,涉及到了Linux编程,这里涉及到了epoll机制,这里可以这样理解,在java层,next方法会阻塞,阻塞的实质就是这里,java层调用的时nativePollOnce方法,next初始时nextPollTimeoutMillis为0,立即返回,不阻塞,随后取一个msg,计算等待时间,下次循环调用nativePollOnce时开始阻塞。(简单介绍一下,需要详细了解的朋友还是要翻看代码)。

这里面还有一点,在没取到消息时,nextPollTimeoutMillis=-1,可能会永久阻塞,但是在MessageQueue 的enqueueMessage方法中,当成功添加后,有一个判断:

if (needWake) {
    nativeWake(mPtr);
}

这个needWake在添加队列中第一个消息时,被赋值,就是mBlocked的值,mBlocked 在next()中需要循环时被置为true。nativeWake就是去唤醒,不再阻塞。起始只有队列为空时再回取到null消息,所以在添加第一个消息到队列时,自然要解除阻塞。可以看一下nativeWake:

static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    nativeMessageQueue->wake();
}

void NativeMessageQueue::wake() {
    mLooper->wake();
}

void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ wake", this);
#endif

    uint64_t inc = 1;
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
    if (nWrite != sizeof(uint64_t)) {
        if (errno != EAGAIN) {
            LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s",
                    mWakeEventFd, strerror(errno));
        }
    }
}

在Looper::rebuildEpollLocked方法中

    eventItem.data.fd = mWakeEventFd;
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);

wake中执行了write(mWakeEventFd, &inc, sizeof(uint64_t)),mWakeEventFd就是epoll监听的事件,通过wake()就解除了阻塞。

分析了这么长,又掺入native部分,总算把取消息的流程说完了,这一部分要多看几遍源码,仔细琢磨。下面我们回到loop方法中。

当取到一个消息,并不为空时,调用了msg.target.dispatchMessage(msg)去分发事件:

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

到这里就回调到了handleMessage,进行了一次选择,如果实现了callback 就走callback ,否则走handleMessage方法。到这里整个发送接收体系就完全介绍完了。

在loop方法最后,调用了msg.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++;
            }
        }
    }

这个方法是在一个Message用完之后调用的,清空了Message的所有内容,并且加入了一个Pool,看着像一个池子,实际上是Message利用next成员建立起的一个链表,为什么要用链表,且看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();
    }

这是一个静态方法,可以代替new Message()获得一个Message实例。方法中,就是取到了链表的头部那个Message,这个链表在每次loop循环后增长,形成缓存池,实现了Message的复用,节省了内存。这个链表最大长度为50(在Android 8.0中),所以日常开发时,我们应该尽可能的调用obtain方法去获得一个Message,实际上源码也是这样做的。

    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }

    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

除了让Looper控制建立缓存,我们也可以自己回收,虽然我们不能调用recycleUnchecked方法(有访问权限控制),但是我们可以调用recycle方法,回收一个Message,他只是做一个安全检查而已。

    public void recycle() {
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();
    }

最后,我们来解释文中的一个遗留问题,为什么ActivityThread这个应用主线程可以有一个死循环。

首先看,如果没有死循环,Looper.loop()执行完之后,直观来看会抛一个异常

new RuntimeException("Main thread loop unexpectedly exited");

实际上,就算不抛异常,那么到这这个main方法就执行完了,你站在java代码角度看,main就执行完,程序不就结束了么?

有过socket开发经验的朋友,肯定不会让服务端接受一个请求后就让main函数结束,一般会一直循环监听请求。

还有另外一个更生动的场景,我们在学习c语言时,第一个程序都是helloworld,有些编译器很智能,打印完helloworld,程序暂停,但有些直接就退出,现象就是我们执行程序时,屏幕闪一下黑框就什么都没了,大多数人都会一脸懵逼的去百度,然后加个暂停或者等待输入的函数,让控制台保留下来,得意的看着自己第一个程序。但实际上程序执行完打印语句后就应该退出了,我们只是让程序阻塞住而已。

android也一样,所以要有一个死循环,保证主线程不自动结束。但为什么要死循环呢?注意一点,loop()方法的循环并不是无意义的循环,而是不断取事件执行事件,Android是建立在事件驱动机制上的,程序在创建运行过程中有很多事件,都是Handler处理的,所以要有一个loop()去驱动事件执行。我们大致可以看下ActivityThread中的Handler方法:



定义了非常多事件,例如第一个LAUNCH_ACTIVITY事件中,就创建了Activity实例。

至于为什么不会ANR,其实ANR并不是一种错误,只是系统认为我们在某些地方耗时太长,造成了流程上的阻塞,是一种检测机制。从上文我们可以知道,Handler是顺序执行事件的,一个事件执行的时间过长,就导致后续事件阻塞,所以必须有ANR机制去检测。换句话说,如果没有ANR检测,其实也是没问题的,只不过会导致界面一直卡着,或者我们执行某个操作长时间无反应而已,正如出现无响应时系统会给我们一个继续等待的选项。而这里的死循环,相当于后台,事件相当于前台,后台阻塞的去监听前台请求是没问题的。

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

推荐阅读更多精彩内容