27
2017
09

Handler消息机制(Java层)

Handler消息机制(Java层)

1.简介

Android应用程序是通过消息来驱动的,系统为每一个应用程序维护了一个消息队列,应用程序的主线程不断地从这个消息队列中获取消息,然后就对这些消息进行处理,这样就实现了通过消息来驱动应用程序的执行。

通过消息驱动的好处是,消息的发送方只要把消息放在应用程序的消息队列中就行了,而不需要等待消息的接收方处理完整个消息才返回,这样就可以提高系统的并发性。

Hanlder与Looper之间的关系

各个元素的关系:

  • Runnable和Message可以被压入某个MessageQueue中,形成一个集合。
  • Looper循环地去做某件事情。
  • Handler是“处理事情的地方”。

一句话概括他们是:

Looper不断获取MessageQueue中的一个Message,然后由Handler来处理

就像中央处理器(Looper)不断从内存(MessageQueue)中读取指令(Message),执行指令(Handler),最终产生结果。

Looper负责创建一个MessageQueue,然后进入无限循环,不断从该MessageQueue中获取一个Message,将Message分发给对应的Handler处理。

Looper的主要作用:

  1. 与当前线程绑定,保证任何一个线程只有一个Looper实例,同时Looper也只有一个MessageQueue。
  2. Looper会不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。

与Windows应用程序的消息处理过程一样,Android应用程序的消息处理机制也是由消息循环消息发送消息处理这三部分组成。接下来将从源码的角度来分析这三个过程。

2.消息循环

在消息处理机制中,消息都是存放在一个消息队列中,而应用程序的主线程就是围绕这个消息队列进入一个无限循环的,直到应用程序退出。如果消息队列中有消息,应用程序的主线程就会把它取出来,并分发给相应的Handler进行处理;如果队列中没有消息,应用程序的主线程就会进入空闲等待状态,等待下一个消息的到来。消息的循环过程是有Looper类实现的,我们先看一下一个典型的Handler的用法:

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();//Looper的准备工作

        mHandler = new Handler() {//创建处理消息的Handler
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();//消息循环
    }
}

2.1 Looper.prepare()

首先从Looper.prepare()方法开始:
public static void prepare() {
prepare(true);
}

private static void prepare(boolean quitAllowed) {
    //ThreadLocal变量是每个线程独有的,可以用来判断是否已经创建过Looper了,保证每个线程只有一个Looper对象
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //将新创建的Looper对象保存在ThreadLocal变量中
    sThreadLocal.set(new Looper(quitAllowed));
}

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


private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);//创建一个消息队列,按照先进先出的原则进出
    mThread = Thread.currentThread();//将当前线程保存在mThread变量中
}

可以看到,在Looper.prepare()方法中,创建了一个Looper对象,并将该Looper对象与当前线程进行关联了,保证一个线程只有一个Looper对象。同时在Looper中,创建了一个MessageQueue。这样的话,当前线程有一个Looper对象,Looper对象中又有一个MessageQueue。关系如下:currentThread->Looper->messageQueue。

MessageQueue的构造方法如下:

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    mPtr = nativeInit();//调用本地方法创建一个NativeMessageQueue
}

MessageQueue的初始化工作交由JNI方法nativeInit来实现了,它的主要工作是创建一个NativeMessageQueue,并把它保存在mPtr变量中。nativeInit方法的实现见Handler消息机制(Native)文章。

2.2 Handler的构建

接下来看Handler的创建:

//默认构造方法
public Handler() {
    this(null, false);
}
/*
 * Handler默认是同步的,除非async值为true。
 * 异步消息代表中断或者事件,不需要与同步消息一起排队。
 * 如果async为true,则handler会给每个Message或Runnable调用setAsynchronous(boolean)方法。
 */
public Handler(Callback callback, boolean async) {
    ....
    //获取Looper对象,该Looper对象是当前线程关联的Looper对象,如果是在主线程,则是主线程的Looper对象
    mLooper = Looper.myLooper();
    //如果Looper对象为空,则抛出异常,因为必须先创建完Looper之后,才能创建Handler
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;//获取Looper对象关联的消息队列
    mCallback = callback;//如果有CallBack,则将CallBack赋值
    mAsynchronous = async;
}

public static @Nullable Looper myLooper() {
    //返回当前线程的Looper对象
    return sThreadLocal.get();
}

还有一种Handler的构造方法:

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

//直接使用参数中的Looper对象
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

从Handler的构造方法中,可以看到,Hanlder主要是为了与Looper对象进行关联,即可以与当前线程的Looper对象关联,也可以与参数中的Looper关联。关联Looper之后,再与Looper的MessageQueue关联。这样的话,Handler就与某个Looper的MessageQueue进行关联了,Handler发送的消息将放入关联的MessageQueue中。

2.3 Looper.loop()

接下来看下Looper.loop()方法

public static void loop() {
    //获取当前线程的Looper对象
    final Looper me = myLooper();
    //如果Looper为空,则抛出异常
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    //获取Looper关联的MessageQueue
    final MessageQueue queue = me.mQueue;

    .......

    //死循环,不断从消息队列中取出消息,然后交给对应的Handler处理。
    for (;;) {
        //取出消息队列中的消息,可能会阻塞
        Message msg = queue.next(); // might block
        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
        //可以用来打印Message开始执行前的信息
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        ....
        try {
            //将消息分发给目标target处理,即交给对应的Handler处理。
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        //可以用来打印Message消息执行后的信息
        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }
        ...
        //回收消息
        msg.recycleUnchecked();
    }
}

这里就是进入消息循环中去了,它不断地从消息队列MessageQueue中去获取下一个要处理的消息msg,如果消息为空,则表示要退出循环了,否则的话就要调用这个消息的target对象的dispatchMessage函数来处理这个消息。

2.4 MessageQueue.next()

在从消息队列中获取消息时,可能会被阻塞,即MessageQueue的next函数会阻塞,它的实现如下:

Message next() {
    final long ptr = mPtr;//获取保存的NativeMessageQueue
    if (ptr == 0) {
        return null;
    }

    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;// 第一次遍历时,等待时间为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;
                    msg.markInUse();//标记消息正在使用
                    return msg;//返回消息队列首部的消息
                }
            } else {
                nextPollTimeoutMillis = -1;//没有消息,则阻塞等待
            }

            //终止,销毁消息队列
            if (mQuitting) {
                dispose();
                return null;
            }
            // 在进入等待状态前,先查看有没有IdleHandler需要执行
            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);
        }

        // 如果有需要执行的IdleHandler,则执行其queueIdle方法
        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);
            }
            // queueIdle方法返回false,则移除该IdleHandler
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
        pendingIdleHandlerCount = 0;
        // 执行完IdleHandler之后,需要将nextPollTimeoutMillis置为0,因为有可能在执行IdleHanlder的时候,已经有新的消息加入了消息队列,因此,需要重置nextPollTimeoutMillis的值。
        nextPollTimeoutMillis = 0;
    }
}

调用next函数时,有可能会让线程进入等待状态。有两种情况会让线程进入等待状态:一是当消息队列中没有消息时,它会使线程进入等待状态;二是消息队列中有消息,但是消息指定的执行时间还没有到这个时间,线程也会进入等待状态。消息队列中的消息是按时间先后来排序的。

nativePollOnce方法是一个JNI方法,表示需要阻塞等待的时间,nextPollTimeoutMillis表示需要等待的时间,为0时表示不等待,为-1时表示一直等待。

当nativePollOnce方法返回后,就去消息队列中查看是否有没有消息,如果消息队列中有消息,并且当前时间大于消息的执行时间,那么直接返回处理该消息。否则的话,就要继续等待到消息的执行时间。

在进入空闲等待状态前,如果应用程序注册了IdleHandler接口来处理一些事件,那么就会先执行这里的IdleHandler,然后再进入等待状态。MessageQueue提供了addIdleHandler和removeIdleHandler两个方法来注册和删除IdleHandler。

可以看到,从MessageQueue中获取Message的过程是,比较当前时间与消息执行的时间,如果当前时间小于消息执行时间,说明还未到消息执行的时机,设置一个超时时间,下次唤醒来执行消息。如果当前时间大于消息执行时间,则直接返回消息队列首部的消息。

3.消息的发送

应用程序的主线程准备好消息队列并且进入到消息循环后,就可以通过Hanlder往这个消息队列中发送消息了。

发送消息的方法有多个,这里我们以sendMessage方法为例来说明。

3.1 Handler.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);
}

不管以何种方式发送消息,最终都会调用到sendMessageAtTime方法。

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;//获取当前Handler关联的MessageQueue。
    //如果队列为空,则抛出异常
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    //将消息入MessageQueue队列
    return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;//将当前Hanlder保存到Message中的target,这样Message与Handler关联了。
    //是否是异步的,在构造Handler的时候,初始化该变量值
    if (mAsynchronous) {
        //将消息类型设置为异步的
        msg.setAsynchronous(true);
    }
    //将消息存入MessageQueue中
    return queue.enqueueMessage(msg, uptimeMillis);
}

发送消息的过程其实就是将消息放入消息队列的过程,在将消息放入消息队列前,会把Handler关联到当前Message,这样当分发消息时,就可以知道处理该消息的Handler。

3.2 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;
        //1.消息队列为空,或者消息执行时间为0或者小于队列首部消息执行时间,则将该消息放入消息队列首部
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
        //2. 将Message插入到队列的中间某个位置,根据时间执行先后排序
            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;
        }

        // 需要立即唤醒
        if (needWake) {
            nativeWake(mPtr);// 调用native方法唤醒
        }
    }
    return true;
}

把消息加入到消息队列时,分两种情况,一种是当前消息队列为空时,这时候应用程序的主线程一般就是处于空闲等待状态,这时候需要唤醒它,此时只要把消息放在消息队列头部就可以了。另外一种情况是应用程序的消息队列不为空,这时候就不需要唤醒应用程序的主线程了,此时按消息的处理时间先后顺序,在消息队列中找到一个合适的位置插入。

当把消息加入消息队列后,如果应用程序的主线程正处于空闲等待状态,则需要调用nativeWake函数来唤醒它了,这是一个JNI方法,主要作用是唤醒应用程序的主线程。

4.消息的处理

在前面的消息循环过程中,当Looper.loop()方法从消息队列中获取到一个消息后,就会调用它的target对象的dispatchMessage函数来处理这个消息。这个target对象是在消息入队列时设置的,具体是在调用Handler的enqueueMessage()方法时设置的。因此消息的处理是调用Handler的dispatchMessage()方法,定义如下:

4.1 Handler.dispatchMessage()

public void dispatchMessage(Message msg) {
    //1.如果Message的CallBack不为空,则直接调用handleCallBack方法
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        //2.如果Handler中的CallBack不为空,则调用CallBack的handleMessage()方法
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //3.如果前面的条件都不满足,则调用handleMessage方法
        handleMessage(msg);
    }
}

//直接调用message的CallBack的run方法
private static void handleCallback(Message message) {
    message.callback.run();
}

//Handler中的callback接口
public interface Callback {
    public boolean handleMessage(Message msg);
}

// Handler中的handleMessage()方法,由子类来实现
public void handleMessage(Message msg) {
}

可以看到,消息的处理的先后顺序是:
Message的CallBack > Handler的CallBack > Handler的handleMessage()方法。

5.ActivityThread的Looper与Handler

我们可以看下主线程的消息处理是怎样的一个过程,在ActivityThread的main()方法中,循环处理消息:

public static void main(String[] args) {
    //初始化执行环境
    Environment.initForCurrentUser();
    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
    TrustedCertificateStore.setDefaultUserDirectory(configDir);
    //设置进程的参数
    Process.setArgV0("<pre-initialized>");

    //准备创建主线程的Looper
    Looper.prepareMainLooper();

    //创建ActivityThread对象
    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        //获取主线程的Handler
        sMainThreadHandler = thread.getHandler();
    }
    ....
    Looper.loop();//开始循环处理消息了

    //如果执行到这里,说明主线程发生了异常,被异常终止了
    throw new RuntimeException("Main thread loop unexpectedly exited");
    }
}

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();//获取当前线程的Looper,因为是在主线程中获取,所以是主线程的Looper
    }
}

final Handler getHandler() {
    return mH;
}

final H mH = new H();
private class H extends Handler {
}

从上面可以看到,在主线程中可以不用执行Looper.prepare()和Looper.loop()方法就可以创建Handler对象,因为在ActivityThread的main方法中,已经处理创建了Looper和MessageQueue,可以直接创建Handler对象。

6.总结

Android应用程序的消息处理机制由消息循环、消息发送和消息处理三个部分组成的。

Looper负责创建一个MessageQueue,然后进入无限循环,不断从该MessageQueue中获取一个Message,将Message分发给对应的Handler处理。

上一篇:第四章 ReactNative与原生之间的接口交互 下一篇:将Android.mk转换成Cmake使用