Handler的前世今生2——Looper

Looper文档


Handler,MessageQueue,Looper这四大金刚,

  1. Message : 消息载体 ;
  2. Handler : 发送和处理消息;
  3. MessageQueue :存储消息;
  4. Looper:传输消息(MessageQueue —> Handler) ;

1. Looper的功能

Handler究其本质就是用来实现线程间通信。在Android开发过程中,我们通常在子线程(Thread) 中将消息发送给 主线程(MainThread), 而 主线程(MainThread) 需要去获取到消息,进而才能去处理消息。

谁来保证主线程(MainThread)获取到消息呢?
Looper,舍我其谁…

源码中对于Looper类的解释,已经很详细啦。

Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call {@link #prepare} in the thread that is to run the loop,and then {@link #loop} to have it process messages until the loop is stopped.
该类用于为一个线程执行消息轮询。默认情况下线程是没有用于消息轮询的looper的,需要在消息轮询的线程中调用prepare()方法来创建一个Looper对象,然后调用loop()去处理消息直到轮询停止。

因为处理消息的线程是只负责处理消息的,压根不知道消息什么时候来。所以才需要Looper不停的刺探军情,为线程去轮询消息


2.Looper的使用前提

Handler的前世今生1——ThreadLocal 谈到TheadLocal来保证Thread和Looper之间忠贞不渝的爱情。

 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));
    }
 private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

一个线程只有一个Looper
一个Looper只有一个MessageQueue.

Thread : Looper : MessageQueue = 1 : 1 : 1

显而易见,Looper 从其对应的MessageQueue 中去轮询消息

2.1 MainLooper

在开发过程中,有时候我们会通过getMainLooper() 来切换到UI线程上。而且我们在主线程中使用Handler时,并没有调用prepare() 和 loop() 等方法。这中间发生了什么有趣的事情呢?

这就需要我们去查看ActivityThread的main()方法。

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

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

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");
		//1. 调用了Looper的prepare()方法
        Looper.prepareMainLooper();

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        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) {
            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();

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

可以看出来,在Looper的使用方法上没有什么区别。

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

  1. getMainLooper()方法
 public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

在Looper里面,我们看到了synchronized + static的实际应用啦。

我们应该理解,

  1. 这里保持同步,因为它是主线程,肯定不能在多线程情况下出现问题;
  2. prepare(false),false 是因为主线程不允许退出,除非应用退出。

3. Looper的loop()

上面讲了那么多,但是都没有切入到Looper的功能。loop()方法才是整个Looper类的核心。调用loop()才表示真正的发车啦。

实际上,我们都说Looper是负责消息轮询的,若没有消息则阻塞,该阻塞的逻辑实现是在MessageQueue的next()方法。以后会展开讨论…

public static void loop() {
		// 1. 取到looper对象
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        // 2. 获取MessageQueue
        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();
		// 3. 无限轮询
        for (;;) {
        	// 这里会阻塞,一定要记住,阻塞逻辑是在MessageQueue中实现的。而不是这里
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
          
            // Make sure the observer won't change while processing a transaction.
            // Observer就是一个监听事件发送过程的接口
            final Observer observer = sObserver;

            Object token = null;
            if (observer != null) {
                token = observer.messageDispatchStarting();
            }
            long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
            try {
            	// 重点在这里,此处的target其实就是Handler
                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);
            }
           
            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            // 消息复用机制
            msg.recycleUnchecked();
        }
    }

3.1. loop()的流程

  1. 获取Looper对象,然后拿到MessageQueue;
  2. 开启轮询,
    2.1. 通过MessageQueuenext() 获取消息;
    2.2. 表面上,若获取的消息为空,则退出循环(可能退出循环吗?不可能的);
    2.3. 通过Message.target(即Handler),调用 dispatchMessage(msg) 进行消息分发和处理。
    2.4 调用MessagerecycleUnchecked() ,进行消息回收。

再次强调:loop()方法没有消息则阻塞,阻塞的逻辑是在MessageQueue的next()实现的。


4. Looper还有退出功能

Looper在没有消息的时候进行阻塞,终归还是要消耗一部分资源的。所以除了主线程,其他线程还是需要退出循环功能的。

PS:值得我们注意的是,退出功能最终也是交由MessageQueue实现的。

 public void quit() {
        mQueue.quit(false);
    }

推荐使用quitSafely()

 public void quitSafely() {
        mQueue.quit(true);
    }

至于两者的区别,跟线程池的退出机制类似…


5. 关于loop()方法的考点

5.1 loop()死循环为什么没有阻塞主线程?

我们会看到ActivityThread的Looper.loop()方法是在main()方法的最后。而Android本身就是靠事件驱动的。所以Looper.loop()不断地接收并处理消息。我们的代码都是在这个循环中执行的,所以当然不会阻塞啦。具体可以自行百度。

5.2 loop()没有消息时是如何处理的?

其实还是想强调一下:loop()的阻塞逻辑是在MessageQueue中实现的。


6. 总结

Looper只是用来负责消息轮询的,与负责发送和接收处理消息的Handler是没有任何关联的。这一点也可以通过源码来证实。涉及到的对应关系如下:

Looper : Thread : MessageQueue = 1 : 1 : 1

Looper : Handler = 1 : N

Looper 和 Handler是没有任何联系的。
Looper 本身的阻塞功能退出功能都是MessageQueue实现的。


欢迎大家继续收看 Handler的前世今生3——Message

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章