Handler,MessageQueue,Message 与Looper

首先来一个整体的印象:

我们将这四个对象看做一条流水线的工作流程,Handler(工人),MessageQueue(传送带),Message(待处理的货物),Looper(传送带的发动机)

工作流程: 工人(Handler)货物(Message) 放到 传送带(MessageQueue)上,发动机(Looper) 带动整个传送带往前走,工人将传送带上的货物取下来打包处理。

流水线要能正常的工作,工人(Handler)首先要有一条传送带,传送带要有发动机,要传送货物当然还得有货物(Message),当然发动机还得启动(Looper.loop())
这个对应了Handler中的几个属性,来看看Handler的属性组成:

    final Looper mLooper;//发动机
    final MessageQueue mQueue;//传送带
    final Callback mCallback;//
    final boolean mAsynchronous;

所以一个正常的Handler要正常工作,所必需的的三大要素:

1.构造一个MessageQueue,这个消息队列用来存储handler的sendMessage(Message msg)传过来的Message。
2.构造发动机唯一的Looper对象(每一条流水线只能有一台发动机),这个looper是整个消息传递机制动力的来源
3.所有工作完成之后,要启动发动机,即Looper要启动,注意looper一经启动就意味着所有的准备工作已经完成,也就是说所有的准备工作必须在looper启动之前,因为looper是个死循环,在这个循环启动之后的代码如果执行了,说明整个系统已经出错了或者退出了。这也就是一般Looper.looper()是写在方法最后一句的原因。

那么问题来了:
1.有人会问,既然looper是个死循环,它不会一直运行导致占用太多的系统资源导致系统内存溢出崩溃吗?

要回答这个问题首先要知道,线程中默认是没有Looer这个东西的,但是如果你需要使用 Handler,那么这个东西还会被创建,那么我们先来看看Looper是在哪里被创建的,又是如何与线程产生联系的。那么首先来看Handler的创建,不多说上源码:

    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class 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(//这里给出了提示,如果没有调用Looper.prepare()方法就运行Handler的话会报错
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;//构造发动机的同时也构造了传送带
        mCallback = callback;
        mAsynchronous = async;
    }

Handler有三个构造函数,但最后会调用上面的,代码很简单,就是简单的构造所需要的组件,下面来看 Looper.myLooper()方法:

    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
..............................
    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal sThreadLocal = new ThreadLocal();

是不是觉得有点熟悉了,对,ThreadLocal!前面讲了ThreadLocal的set与get方法可以设置变量为线程的私有变量。那么我们去看看sThreadLocal 的set方法在哪调用:

 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));//这里简单的new了一个looper对象
    }
..........................
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//创建了messageQueue对象
        mThread = Thread.currentThread();
    }

这也就是我们前面提到的在创建Handler之前不调用Looper.prepare()方法会报错的原因。因为sThreadLocal.get()拿到的是空值!这样looper与当前线程就形成了逻辑上的一对一的关系

了解了looper的创建我们来看看Android的主线程,Android应用的入口在ActivityThread类,我们来看看它的main函数:

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

     Process.setArgV0("");

        Looper.prepareMainLooper();//准备主循环

        ActivityThread thread = new ActivityThread();//创建ActivityThread对象,其中会创建一个H 对象,其实也就是我们说的handler对象
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
      ...........................
        Looper.loop();//启动主循环

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

     final H mH = new H();//这是activityThread中创建的H对象

从上面我们可以知道,looper的创建是在 Handler 创建之前的,当一切准备就绪之后,调用Looper.loop()应用程序就进入了死循环。

回到上面的问题,既然主线程进入了死循环,那么这个死循环会不会导致应用卡死,即使不会卡死会不会过度的耗费系统资源(CPU)?
答案大家都知道 是肯定不会,但是是为什么呢?
就我个人的理解哈,设计成死循环的目的,是让主线程能一直运行下去(除非用户自己退出),我们也不想我们的应用程序运行一段时间就自动退出吧?出于这个考虑,死循环是最好的选择。其实binder内部也是采用的死循环。再回过头来看Looper.looper方法:

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;//获取当前的消息队列

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

        for (;;) {//死循环 一个个去取出消息
            Message msg = queue.next(); // might block不断去取messageQueue中的消息,这个地方有可能阻塞
            if (msg == null) {//如果取到的消息为空,则说明消息队列在退出了
                // No message indicates that the message queue is quitting.
                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);
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

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

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            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的工作过程,首先拿到当前线程的looper,然后通过这个looper拿到当前的messageQueue,然后进入死循环,不断的去取messageQueue中的消息,即调用messageQueue中的next方法,这个方法有可能会阻塞,也就是主线程休眠。如果next方法取到的值为 null ,那么表示消息队列在退出了,也就是整个线程在退出了

这里的next方法的结果只会有三种状态
1.返回正常值
2.阻塞,表示队列中没有消息,主线程进入休眠
3.返回null,表示线程在退出

上面说了,在队列中没有消息时(实际判断没这么简单),主线程会进入休眠,因此也不至于过度的耗费系统资源。

简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

2.没看见哪里有相关代码为这个死循环准备了一个新线程去运转?

这个我们其实在最初接触android的时候就知道,当一个安卓应用启动的时候,,每个App进程中至少会有两个binder线程 ApplicationThread(简称AT)和ActivityManagerProxy(简称AMP),除了图中画的线程,其中还有很多线程,比如signal catcher线程等。

事实上,会在进入死循环之前便创建了新binder线程,在代码ActivityThread.main()中:

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);//便会创建一个Binder线程

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

thread.attach(false);便会创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程

3. Activity的生命周期是怎么实现在死循环体外能够执行起来的?

前面说到了ActivityThread中H对象,ActivityThread的内部类H继承于Handler,通过handler消息机制,简单说Handler机制用于同一个进程的线程间通信,而Binder是用于进程中通信!

Activity的生命周期都是依靠主线程的Looper.loop,当收到不同Message时则采用相应措施:在H.handleMessage(msg)方法中,根据接收到不同的msg,执行相应的生命周期。 比如收到msg=H.LAUNCH_ACTIVITY,则调用ActivityThread.handleLaunchActivity()方法,最终会通过反射机制,创建Activity实例,然后再执行Activity.onCreate()等方法; 再比如收到msg=H.PAUSE_ACTIVITY,则调用ActivityThread.handlePauseActivity()方法,最终会执行Activity.onPause()等方法。 上述过程,我只挑核心逻辑讲,真正该过程远比这复杂。

主线程的消息又是哪来的呢?当然是App进程中的其他线程通过Handler发送给主线程,请看接下来的内容:

Handler,MessageQueue,Message 与Looper_第1张图片
Activity的生命周期通信.jpg

结合图说说Activity生命周期,比如暂停Activity,流程如下:

1.线程1的AMS中调用线程2的ATP;(由于同一个进程的线程间资源共享,可以相互直接调用,但需要注意多线程并发问题)
2.线程2通过binder传输到App进程的线程4;
3.线程4通过handler消息机制,将暂停Activity的消息发送给主线程;
4.主线程在looper.loop()中循环遍历消息,当收到暂停Activity的消息时,便将消息分发给ActivityThread.H.handleMessage()方法,再经过方法的调用,最后便会调用到Activity.onPause(),当onPause()处理完后,继续循环loop下去。

总结来说就是:

你可能感兴趣的:(Handler,MessageQueue,Message 与Looper)