源码分析Android的消息机制

一、引言

​Android消息机制主要指的是Handler的运行机制,是一块很有意思,也很有研究意义的内容。本文计划在较短的篇幅内,通过一定的源码,分析Android消息机制,并在结尾说点”题外话“,帮助我们理解消息机制在安卓应用中的作用。

二、Android消息队列与四大组件的关系

​Android的消息机制伴随着安卓四大组件的生命周期,Activity启动,Service绑定与注册,Broadcast广播的发送,ContentProvider的启动等过程,先是依靠Binder将主线程切到Binder线程池的线程,系统完成组件对象的创建、绑定以及其他的准备工作后,再通过消息机制将线程切回至主线程,在主线程调用组件的生命周期方法。例如Activity、Service的onCreate(),ServiceConnection的onServiceConnected()等方法,就是系统通过发送消息给消息队列,由消息队列回调在使得这些方法运行在主线程。

​另外由于Android开发规范的限制,我们并不能在子线程中访问UI控件,通过主线程(也就是UI线程)的Handler,可以将更新UI的操作切换到主线程中执行。

如果我们要用一句话来总结安卓消息机制的作用:极大地简化在子线程中访问主线程操作,更容易地在主线程中控制四大组件的生命周期,也使得UI线程对于用户的响应更加高效、流畅。

三、Android消息机制分析

​在引言我们说了,消息机制主要指的是Handler的运行机制,而Handler运行需要底层的MessageQueueLooper的支撑,那么这三者的关系如何呢?相互之间是怎么配合的?这里我们先给出结论:Handler发送消息到MessageQueue中存储,Looper从MessageQueue中取出消息并执行。 接下来我们逐步通过源码进行分析。

3.1 MessageQueue

​MessageQueue,也就是消息队列,主要包含两个操作:插入和读取。插入和读取对应的方法分别为enqueueMessagenext,其中读取操作同时还伴随着消息的移除操作。我们说消息队列,然而MessageQueue的内部并不是通过队列来存储消息的,为了提高消息插入和删除的效率,实际上MessageQueue是通过单链表的形式来维护消息列表的,我们来看看enqueueMeesage()的源码:

3.1.1 enqueueMessage
boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {	// target就是此消息的接收方
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {			// msg是否正在被使用
        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;			// 是否需要唤醒线程
        // 如果消息队列为空(p = null),或者新消息是一个即时消息
        if (p == null || when == 0 || when < p.when) {
            // 把msg作为消息队列的新头部,第一个执行
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;	// 如果mBlocked = true,线程阻塞了,就需要唤醒线程
        } else {
            // 向单链表插入一条延时消息:根据消息的延时时间when,将消息插入链表一个合适的位置
          	// 原理是when越小,消息所处的位置越靠前
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
              	// 找到链表中第一个比when大的位置
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
          	// 插入new msg
            msg.next = p; 
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {	
            nativeWake(mPtr);		// 唤醒线程
        }
    }
    return true;
}
复制代码

​通过源码中的注释我们已经能够理解enqueueMessage的插入过程,接下来我们看next():

3.1.2 next()
Message next() {
    // 当调用Looper的quit()方法后,即Looper停止后,mPtr为0
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }

    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }

      	// 阻塞方法,nextPollTimeoutMillis为最长阻塞时间
      	// nextPollTimeoutMillis = 0, 不阻塞,立即往下执行
      	// nextPollTimeoutMillis = -1, 一直阻塞,不会超时
      	// nextPollTimeoutMillis > 0,  最长阻塞nextPollTimeoutMillis(ms),如果期间线程被唤醒,立即往下执行
        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()发送的消息,是个同步屏障
              	// looper遇到同步屏障后会忽略同步消息,只处理异步消息
              	// 同步屏障实际上是让消息机制优先处理异步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
          
            if (msg != null) {
                if (now < msg.when) {
                    // 当前这个消息还没到指定的处理时间时,继续延迟,剩余延迟时间 msg.when - now
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    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;
            }

            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }
						
          	// 如果执行到这里,说明单链表中没有消息,接下来处理一些不紧急的任务(Idle Handler)
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
          
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }

            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        // Run the idle handlers.
        // We only ever reach this code block during the first iteration.
        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);
                }
            }
        }
        // Reset the idle handler count to 0 so we do not run them again.
        pendingIdleHandlerCount = 0;
        // While calling an idle handler, a new message could have been delivered
        // so go back and look again for a pending message without waiting.
        nextPollTimeoutMillis = 0;
    }
}
复制代码

​可以看到next()是一个无线循环的方法,如果队列中没有消息,那么next()会一直阻塞在这里。当有新消息到来时,next()方法会返回这条消息并将其从单链表中移除。

3.2 Looper

​我们分析了MessageQueue主要是对消息进行插入和移除,分别使用enqueueMessage()和next()方法,那谁来调用消息队列的这些方法呢,其实就是Looper,Looper在消息机制中扮演着消息循环的角色,具体来说就是它会不停地从MessageQueue中查看是否有新消息,如果有新消息就会立刻处理,否则就一直阻塞在那里。

​Looper和MessageQueue关系很紧密,在Looper的构造方法中,它会创建一个MessageQueue对象,并将该对象保存起来:

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

​可以看到,作为Looper中唯一的一个构造方法,这个方法居然是private的,仅允许类内部创建它的实例。我们如果要创建一个Looper,需要调用它的prepare()方法,Looper除了prepare()以外,还提供了prepareMainLooper(),这个方法主要是给主线程(ActivityThread)创建Looper使用的,但本质上还是调用的prepare(),接下来我们看看prepare()方法的源码吧:

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对象,它并不是线程,它的作用是可以在每个线程中存储数据,不同线程的数据互不干扰。在消息机制中,ThreadLocal就存储了在不同线程中创建的Looper对象,这些对象是互不干扰的,我们通过ThreadLocal.get()方法就可以得到当前线程中的Looper对象。关于当前属于哪个线程的判断,由get()方法内部处理,这里我们不对ThreadLocal做过多的分析。

​同时,我们可以看出,在一个线程中只有一个Looper对象,重复创建会报运行时异常。Looper对象只会创建一次,因此在Looper构造方法内部中的MessageQueue对象也只有一个,而Handler是可以多次创建的。

​接下来我们看看Looper中最重要的loop()方法,只有调用了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.");
    }
  	// mQueue对象
    final MessageQueue queue = me.mQueue;

    // ...

    for (;;) {
        // 调用mQueue.next()读取消息队列中的消息,没有新消息的话就会堵塞在这
        Message msg = queue.next(); // might block
      	// loop()方法唯一出口
        if (msg == null) {
            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);
        }
      
        // ....
      
        try {
          	// 在Looper中调用Handler的dispatchMessage()方法,使得将代码逻辑切换到Looper所在线程中执行
            msg.target.dispatchMessage(msg);
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
            if (observer != null) {
                observer.dispatchingThrewException(token, msg, exception);
            }
            throw exception;
        } finally {
            ThreadLocalWorkSource.restore(origWorkSource);
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        
      	// ....
    }
}
复制代码

​Looper的loop方法也很好理解,我们省去一些不必要的代码。可以看出,loop()实际上也是一个死循环,唯一跳出循环的方式就是MessageQueue的next()方法返回null。当Looper的退出方法被调用时,Looper就会调用MessageQueue的quit(boolean)方法,将队列的mQuitting变量置为true,这样当MessageQueue执行下一次循环的时候会发现mQuiting标志为true,进而使得next()返回null,Looper的loop()也随之跳出循环结束。读者可以结合MessageQueue的next()源码,更容易理解loop的退出过程。

​Looper提供了两个退出方法:quit()和quitSafely()。二者的区别是,quit()会直接退出Looper,而quitSafely只是设定一个退出标记,然后把消息队列中已有的消息处理完毕后才安全的退出。Looper退出后,通过Handler发送的消息会失败,这个时候Handler的send()方法会返回false。

在子线程中,如果我们手动为其创建了 Looper,那么在所有的事情完成以后应该调用 quit() 方法来终止消息循环,否则 looper 会等待新的消息到来而一直处于阻塞状态,这个子线程因此一直处于等待状态,而如果退出Looper后,这个线程就会立刻终止,所以建议在不需要Looper的时候终止它,减少不必要的线程开支。

​再回头看看我们loop()的源码,里面还有一句msg.target.dispatchMessage(msg),msg.target 就是我们的Handler对象,我们在Looper中调用Handler.dispatchMessage()方法,那么Looper在哪个线程中创建,dispatchMessage()就会运行在哪个线程中,这样,一个消息取出后,就被切换到指定的线程中处理了。

3.3 Handler

​在分析Handler工作前,我们先来看看Handler有哪些构造方法:

public Handler() {														// 无参构造方法,默认是同步Handler
  	this(null, false);
}
复制代码
public Handler(@Nullable Callback callback) {	// 指定一个callback
    this(callback, false);
}
复制代码
public Handler(@NonNull Looper looper) {			// 指定一个looper,一般在子线程中调用
    this(looper, null, false);
}
复制代码
public Handler(@NonNull Looper looper, @Nullable Callback callback) {	// 指定looper, callback
    this(looper, callback, false);
}
复制代码
public Handler(boolean async) {								// async = true,创建一个处理异步消息的Handler
    this(null, async);
}
复制代码
public Handler(@Nullable Callback callback, boolean async) {		// 用当前线程的Looper来创建Handler
    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;
}
复制代码
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
复制代码

​从上面这些构造方法,我们可以自己指定Looper对象来创建Handler,可以创建一个专门处理发送异步消息的Handler,同时我们也可以给Handler设置Callback回调来创建Handler,如果调用的是无参构造方法,那么系统就使用当前线程的Looper对象来创建一个Handler对象,一个线程可以创建多个Handler对象。

​Handler的工作主要是消息的发送和接受。消息的发送可以通过post的一系列方法以及send的一系列方法来实现,大部分的发送方法,它们最终都会来到sendMessageAtTime(),我们来看看它的源码:

public boolean sendMessageAtTime(@NonNull 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(),我们跟进一下看看:

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this;		// 同步消息,target指向自己
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码

​在enqueueMessage()内部,我们给msg设置了target,就是当前的Handler对象,同时调用mQueue的enqueueMessage()方法,将消息插入到消息队列中。

3.3.1 dispatchMessage()

​Looper收到消息后最终会调用Handler的dispatchMessage()进行处理,这时Handler就进入了处理消息的阶段,我们来看看dispatchMessage的具体实现:

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

​首先,检查Message的callback是否为null,不为null就通过handleCallback来处理消息,Message的callback是一个Runnable对象,实际上就是Handler的post()方法所传递Runnable参数。

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

handleCallback(msg)的内部实现也很简单,就是调用Runnable的run()方法,这里就不贴代码了。

​接下来检查mCallback是否为空,这个mCallback就是我们创建Handler时可选的Callback对象,Callback是个接口,里面只有handleMessage()一个待实现的方法:

public interface Callback {
        boolean handleMessage(@NonNull Message msg);
}
复制代码

​值得注意的是,如果mCallback.handleMessage(msg)返回的是false,那么还是会执行到Handler的handleMessage()方法。具体怎么利用方法的返回值,就要看具体的使用场景了,一般很少有这两个方法同时使用的场景。

实际上,通过Callback,我们可以采用如下方式来创建Handler对象:Handler handler = new Handler(callback)。那么这样做的意义是什么呢?在日常开发中,创建Handler最常见的方式就是派生一个Handler的子类,并重写其handlerMessage()方法来处理具体的消息,而Callback给我们提供了另一种使用Handler的方式,当我们不想派生子类时,就可以通过Callback来实现。

​我们将Handler处理消息个过程可以总结为一个流程图,如下所示:

四、“题外话“来了

​Android应用本质上是在手机上的一个事件驱动型进程,应用启动的入口是ActivityThread.main()方法。当我们用手点击启动一个App,实际上就是让系统顺序执行这个App的main()方法,在方法中,系统会为这个App创建主线程的Looper以及MessageQueue,并通过Looper.loop()来开启主线程的消息循环,Looper.loop()不断读取MessageQueue中的消息并处理,如果消息队列中没有消息了,主线程就会阻塞,直到新消息的到来。因此一个应用进程的启动后会一直停留在Looper.loop()这一行,直到消息队列收到进程退出的消息,由消息队列调用进程的退出,或者主进程被系统杀死,应用才算真正的退出。一个应用启动了便不能自己退出,必须借助“外力”。

​说起来还是有点抽象,下面请看main()的完整代码,它并没有我们想象中的那么长:

public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

    // Install selective syscall interception
    AndroidOs.install();

    CloseGuard.setEnabled(false);

    Environment.initForCurrentUser();

    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
    TrustedCertificateStore.setDefaultUserDirectory(configDir);

    Process.setArgV0("<pre-initialized>");
		
  	// 准备主线程的Looper
    Looper.prepareMainLooper();

    long startSeq = 0;
    if (args != null) {
        for (int i = args.length - 1; i >= 0; --i) {
            if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                startSeq = Long.parseLong(
                        args[i].substring(PROC_START_SEQ_IDENT.length()));
            }
        }
    }
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    if (sMainThreadHandler == null) {
      	// 创建主线程的Handler
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 		// 最后一行代码,开启消息循环
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}
复制代码

​看到这里,题外话就说完了,不过,看完main()方法的我,突然想到一个有意思的问题:Looper.loop()既然是一个死循环,消息队列中没有消息时便会阻塞,那为什么它没有在主线程中导致ANR错误呢!?

​多么有意思的问题!~ 这个问题实际上在网上已经有答案了,我在这就给大家卖个关子,把问题当做本文的结尾了,大家自行去网上冲浪吧

兄dei,如果觉得我写的还不错,麻烦帮个忙呗 :-)

  1. 给俺点个赞被,激励激励我,同时也能让这篇文章让更多人看见,(#^.^#)
  2. 不用点收藏,诶别点啊,你怎么点了?这多不好意思!

拜托拜托,谢谢各位同学!

猜你喜欢

转载自juejin.im/post/7109656747748524062