Android Handler机制之Handler 、MessageQueue 、Looper

很随意.jpg

该文章属于《Android Handler机制之》系列文章,若是想了解更多,请点击 《Android Handler机制之总目录》bash

前言

上篇文章,咱们讲了ThreadLocal,了解了线程本地变量的实质,若是有小伙伴仍是不熟悉ThreadLocal原理的,请参看上篇文章《 Android Handler机制之ThreadLocal》。若是你已经阅读 了该文章,那如今咱们就一块儿来了解Handler与MessageQueue与Looper三者之间的关系及其内部原理。async

Handler、MessageQueue、Looper三者之间的关系

在了解其三者关系以前,我先给你们一个全局的关系图,接下来的文章会根据该关系图,进行相应的补充与描述。 ide

HandlerLooperMessage关系.png

从上图中咱们能够看出几点函数

  • Handler的建立是与Looper建立的线程是相同的。
  • Looper中内部维护了一个MessageQueue(也就是消息队列)。且该队列是经过链表的形式实现的。
  • Hanlder最终经过sendMessage方法将消息发送到Looper中对应的MessageQueue中。
  • Looper经过消息循环获取消息后,会调用对应的消息中的target(target对应的是发消息的Handler)的dispatchMessage()方法来处理消息。

Looper原理

由于消息队列(MessageQueue的建立是在Looper中内部建立的,同时Handler消息的发送与处理都是围绕着Looper来进行的,因此咱们首先来说Looper。oop

Looper是如何与主线程关联的

在平时开发中,咱们使用Handler主要是为了在主线程中去更新UI,那么Looper是如何与主线程进行关联的呢?在Android中,App进程是由Zygote fork 而建立的,而咱们的ActivityThread就是运行在该进程下的主线程中,那么在ActivityThread的main方法中,Looper会经过prepareMainLooper()来建立内部的消息队列(MessageQueue),同时会经过loop()构建消息循环。具体代码以下图所示:post

public static void main(String[] args) {
		...省略部分代码
        Looper.prepareMainLooper();//
        
        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        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");
    }
复制代码

要了解当前Loooper如何与主线程进行关联的,须要继续查看prepareMainLooper()方法。下述代码中,为了你们方便,我将prepareMainLooper()方法所涉及到的方法所有罗列了出来。ui

//建立主线程Looper对象
   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与当前主线程绑定
  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));//建立Looper对象,放入主线程局部变量中
    }
    
  //获取当前主线程的Looper对象
  public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
   }

复制代码

观察上诉代码,咱们发现,prepareMainLooper方法内部调用prepare()方法(这里咱们忽略该方法中的参数 quitAllowed),而prepare内部调用的是ThreadLocal的set()方法。若是你阅读了以前我写的《 Android Handler机制之ThreadLocal》,那么你们应该知道了当前Looper对象已经与主线程关联了(也能够说,当前主线程中保存了当前Looper对象的引用)。this

Looper内部建立消息队列

在了解了Looper对象怎么与当前线程关联的后,咱们来看看Looper类中的具体方法。以前咱们说过,在建立Looper对象的时候,当前Looper对象内部也会建立与之关联的消息队列(MessageQueue)。那么查看Looper对应的构造函数:spa

final MessageQueue mQueue;
	//Looper内部会建立MessageQueue对象
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
复制代码

从Looper对象的构造函数中,咱们很明显的看出内部建立了MessageQueue对象,也验证了咱们以前的说法。线程

Looper的消息循环

当前Looper对象与主线程关联后,接着会调用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;
		...省略部分代码
        for (;;) {//一直循环去获取消息队列中的消息
            Message msg = queue.next(); //该方法可能堵塞,
            if (msg == null) {
	            //若是没有消息,表示当前消息队列已经退出
                return;
            }

        ...省略部分代码
            try {
             //获取消息后,执行发送消息的handler的dispatchMessage方法。
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
        ...省略部分代码
            }

            msg.recycleUnchecked();
        }
    }
复制代码

经过上述代码,咱们能够看出,在Looper中的loop()方法中会一直去拿当前消息队列中的消息,若是能取出消息会调用该消息的target去执行dispatchMessage()方法。若是没有消息,就直接退出消息循环。

MessageQueue原理

MessageQueue的next()方法

由于Looper中loop()方法会循环调用MessageQueue中的next方法,接下来带着你们一块儿查看该方法。代码以下图所示:

Message next() {
	...省略部分代码
	 for (;;) {
	     synchronized (this) {
	     ...省略部分代码
         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;
                    }
                }
              ...省略部分代码
        }
      }
    }
复制代码

上述代码中,我省略了不少代码,如今你们不须要关心省略的内容,你们只要关心大的一个方向就够了,关于MessageQueue的next()具体详解,会在下篇文章《Android Handler机制之Message的发送与取出》具体介绍。好了,你们把状态调整过来。 在上文中,咱们说过MessageQueue是以链表的形式来存储消息的,从next()方法中咱们能分析出来,next()方法会一直从MessageQueue中去获取消息,直到获取消息后才会退出。

MessageQueue的enqueueMessage()方法

经过上文,咱们已经了解Message取消息的流程,如今咱们来看看消息队列的加入过程。

boolean enqueueMessage(Message msg, long when) {
	      ...省略部分代码
        synchronized (this) {
          ...省略部分代码
            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 {
              ...省略部分代码
                //循环遍历消息队列,把当前进入的消息放入合适的位置(比较等待时间)
                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;
            }

	      ...省略部分代码
        }
        return true;
    }
复制代码

上诉代码中,咱们把重心放在for循环中,在for循环中主要干了 一件事,就是根据当前meesag.when的值,来肯定当前插入的消息应该放入消息队列的位置。(当前小伙伴肯能会对message.when感到困惑,仍是那句话,现阶段咱们只用关心主要的流程,具体的方法详解会在下篇文章《Android Handler机制之Message的发送与取出》具体介绍)

Handler的原理

了解了Looper与MessageQueue的原理后,咱们大体了解了整个消息处理的关系,如今就剩下发消息与处理消息的流程了。最后一点了,你们坚持看完。

Handler是怎么与Looper进行关联的

在文章最开始的图中,Handler发消息最终会发送到对应的Looper下的MessageQueue中。那么也就是说Handler与Looper之间必然有关联。那么它是怎么与Looper进行关联的呢?查看Handler的构造函数:

//不带Looper的构造函数
  public Handler() {this(null, false);}
  public Handler(boolean async) {this(null, async);}
  public Handler(Callback callback) {this(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());
            }
        }
		//Looper.myLooper()内部会调用sThreadLocal.get(),获取线程中保存的looper局部变量
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
	    //获取当前Looper中的MessageQueue
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    
 //带Looper参数的构造函数
  public Handler(Looper looper) { this(looper, null, false); }
  public Handler(Looper looper, Callback callback) { this(looper, callback, false);}
  public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        //获取当前Looper中的MessageQueue
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }  
复制代码

在Handler的构造函数中,主要分为两种类型的构造函数,一种是带Looper参数的构造函数,一种是不带Looper参数的构造函数。

  • 在不带Looper参数的构造函数中,是经过Looper.myLooper()来获取当前Looper对象的(也就是说,Handler获取的Looper对象是与当前实例化当前Handler的线程相关的,那么若是Handler对象是在主线程中建立的,那么获取的就是主线程的Looper,注意前提条件当前线程线程已经经过Looper.prepare()与Looper.loop()构建了循环消息队列,由于只有调用了该方法后,才会将当前Looper对象放入线程的局部变量中
  • 在带Looper参数的构造函数中,Looper对象是经过外部直接传入的。(这里其实给咱们提供了一个思路,也就是咱们能够构建本身的消息处理循环,具体细节参看类HandlerThread)

Handler怎么将消息发送到MessaageQueue(消息队列)中去

在了解Handler怎么将消息发送到MessageQueue(消息队列),咱们先来了解Handler的发消息的系列方法。

//发送及时消息
public final boolean sendMessage(Message msg)
public final boolean sendEmptyMessage(int what)
public final boolean post(Runnable r)

//发送延时消息
public final boolean sendEmptyMessageDelayed(int what, long delayMillis)
public final boolean sendMessageDelayed(Message msg, long delayMillis)
public final boolean postDelayed(Runnable r, long delayMillis)

//发送定时消息
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
public final boolean postAtTime(Runnable r, long uptimeMillis)
复制代码

在Handler发消息的方法中,咱们能够总共发消息的种类,分为三种状况,第一种是及时消息,第二种是发送延时消息,第三种是定时消息。其中关于消息怎么在消息队列中排列与处理。具体的方法详解会在下篇文章《Android Handler机制之Message的发送与取出》具体介绍。

经过查看Handler发送消息的几个方法。咱们发现内部都调用了MessageQueue的enqueueMessage()方法。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//设置message.target为当前Handler对象
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);//获取当前MessageQueue.将消息加入队列中
    }
复制代码

该方法内部其实很简单,就是获取当前MessageQueue对象,将消息将入消息队列中去了。其中须要你们须要的注意的是这段代码msg.target = this。该代码意思就是当前的消息保存着当前发送消息的Handler对象的应用。该行代码很是重要。由于最后涉及到消息的处理。

Handler怎么处理消息

经过上文的描述,如今咱们已经大体了解Handler是怎么将消息加入到消息队列中去了,如今须要关心的是当前消息是怎么被处理的。你们还记的以前咱们讲过的Looper原理吧,Looper会调用loop()方法循环的取消息。当取出消息后会调用message.target.dispatchMessage(Message msg)方法。其中message.target从上文咱们已经知道了,就是当前发送消息的Handler。那么最终也就会回到Handler中的dispatchMessage()方法。

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {//第一步,判断msg.callback
            handleCallback(msg);
        } else {
            if (mCallback != null) {//第二步、判断Handler的callBack
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);//第三步,执行Handler的handleMessage方法
        }
    }
复制代码

观察该方法,咱们能够得出,Handler中的dispatchMessage()方法主要处理了三个步骤,下面分别对这三个步骤进行讲解

第一步,执行message.callback

在Handler中的dispatchMessage()方法中,咱们已经知道若是msg.callback != null,那么咱们会直接走handleCallback(msg)方法。在了解该方法以前,首先咱们要知道msg.callback对于的类是什么。这里我就直接给你们列出来了。其实msg.callback对应是如下四个方法的Runnable对象。

public final boolean post(Runnable r)
public final boolean postAtTime(Runnable r, long uptimeMillis)
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
public final boolean postDelayed(Runnable r, long delayMillis)
复制代码

以上四个方法在发送Runnable对象时,都会调用getPostMessage(Runnable r) 方法,且该方法都会将Runnable封装在Message对象的callback属性上。具体以下getPostMessage(Runnable r) 方法所示:

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
复制代码

在了解了Message的callback到底什么事后,咱们再来看看handleCallback(Message message)方法

private static void handleCallback(Message message) {
        message.callback.run();
    }
复制代码

该方法其实很简单,就是调用相应Runnable的run()方法。

第二步,执行Handler的callBack

若是当前Message.callback为空,接下来会判断Handler中的Callback回调是否为空,若是不为空则执行Callback的handleMessage(Message msg)方法。Callback的具体声明以下:

//避免建立Handler对象从新HandlerMessage方法,你能够直接传入Callback接口实现
  public interface Callback {
        public boolean handleMessage(Message msg);
    }
复制代码

其中在Handler的几个构造函数中,能够传入相应Callback接口实现。

public Handler(Callback callback) 
public Handler(Looper looper, Callback callback) 
public Handler(Callback callback, boolean async)
public Handler(Looper looper, Callback callback, boolean async)
复制代码

第三步,执行Handler的handleMessage)

若是都不知足上面描述的第1、第二状况时,会最终调用Handler的handleMessage(Message msg)方法。

//Handler内部该方法是空实现,须要子类具体实现
  public void handleMessage(Message msg) {  }
复制代码

为了方便你们记忆,我将Handler中的dispatchMessage()具体的逻辑流程画了出来。你们按需观看。

dispatchMessage步骤.png

最后

看到最后你们已经发现该篇文章主要着重于将Handler机制的整个流程,对于不少的代码细节并无过多的描述,特别是关于Looper从MessageQueue(消息队列)中取消息与MessageQueue(消息队列)怎么放入消息的具体细节。不用担忧,关于这两个知识点将会在下篇文章《Android Handler机制之Message的发送与取出》具体描述。

相关文章
相关标签/搜索