Android Handler机制-Looper、Handler、MessageQueue、Message的关系

1、概述

Handler是Android中处理异步消息的机制。Looper、Handler、MessageQueue、Message归纳来讲就是:Looper负责的就是建立一个MessageQueue,而后进入一个无限循环体不断从该MessageQueue中读取消息Message,而后回调相应的消息处理函数,而消息的建立者就是一个或多个Handler,执行完成一个消息后则继续循环。android

2、MessageQueue详解

消息队列MessageQueue就是存放消息的队列。那队列中存储的消息是什么呢?假设咱们在UI界面上单击了某个按钮,而此时程序又刚好收到了某个广播事件,那咱们如何处理这两件事呢? 由于一个线程在某一时刻只能处理一件事情,不能同时处理多件事情,因此咱们不能同时处理按钮的单击事件和广播事件,咱们只能挨个对其进行处理,只要挨个处理就要有处理的前后顺序。为此Android把UI界面上单击按钮的事件封装成了一个Message,将其放入到MessageQueue里面去,即将单击按钮事件的Message入栈到消息队列中,而后再将广播事件的封装成以Message,也将其入栈到消息队列中。也就是说一个Message对象表示的是线程须要处理的一件事情,消息队列就是一堆须要处理的Message的池。线程Thread会依次取出消息队列中的消息,依次对其进行处理。spring

MessageQueue中有两个比较重要的方法,一个是enqueueMessage方法,一个是next方法。enqueueMessage方法用于将一个Message放入到消息队列MessageQueue中,next方法是从消息队列MessageQueue中阻塞式地取出一个Message。bash

3、Looper详解

消息队列MessageQueue只是存储Message的地方,真正让消息队列循环起来的是Looper,这就比如消息队列MessageQueue是个水车,那么Looper就是让水车转动起来的河水,若是没有河水,那么水车就是个静止的摆设,没有任何用处,Looper让MessageQueue动了起来。多线程

Looper是用来使线程中的消息循环起来的。默认状况下当咱们建立一个新的线程的时候,这个线程里面是没有消息队列MessageQueue的。为了可以让线程可以绑定一个消息队列,咱们须要借助于Looper:首先咱们要调用Looper的prepare方法,而后调用Looper的loop方法。异步

(一)prepare()方法

public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(true));
复制代码

sThreadLocal是一个ThreadLocal对象,能够在一个线程中存储变量。能够看到,将一个Looper的实例放入了ThreadLocal,而且先判断了sThreadLocal.get是否为null,不然抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例。async

(二)构造函数

上面的代码执行了Looper的构造函数,咱们看一下其代码:ide

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

在构造函数中,建立了一个消息队列MessageQueue,并将其赋值给其成员字段mQueue,这样Looper也就与MessageQueue经过成员字段mQueue进行了关联。函数

(三)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;
        
        // 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
            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
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
 
            msg.target.dispatchMessage(msg);
 
            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.recycle(); } } 复制代码

上面有几行代码是关键代码:oop

1. final Looper me = myLooper();post

myLooper()方法直接返回了sThreadLocal存储的Looper实例,若是me为null则抛出异常,也就是说looper方法必须在prepare方法以后运行。

final Looper me = myLooper();
if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
复制代码
public static Looper myLooper() {
    return sThreadLocal.get();
}
复制代码

2. final MessageQueue queue = me.mQueue;

拿到该looper实例中的消息队列mQueue。变量me是经过静态方法myLooper()得到的当前线程所绑定的Looper,me.mQueue是当前线程所关联的消息队列。

3. for (;;)

进入了循环。咱们发现for循环没有设置循环终止的条件,因此这个for循环是个无限循环。

4. Message msg = queue.next(); // might block

取出一条消息,若是没有消息则阻塞。咱们经过消息队列MessageQueue的next方法从消息队列中取出一条消息,若是此时消息队列中有Message,那么next方法会当即返回该Message,若是此时消息队列中没有Message,那么next方法就会阻塞式地等待获取Message。

5. msg.target.dispatchMessage(msg);

msg的target属性是Handler,该代码的意思是让Message所关联的Handler经过dispatchMessage方法让Handler处理该Message。

6. msg.recycle();

释放消息占据的资源。

(四)Looper主要做用

1.与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。

2.loop()方法,不断从MessageQueue中去取消息,交给消息Message的target属性,即Handler的dispatchMessage去处理。

2、Handler详解

(一)构造函数

使用Handler以前,咱们都是初始化一个实例,好比用于更新UI线程,咱们会在声明的时候直接初始化,或者在onCreate中初始化Handler实例。因此咱们首先看Handler的构造方法,看其如何与MessageQueue联系上的,它在子线程中发送的消息(通常发送消息都在非UI线程)怎么发送到MessageQueue中的。

public Handler() {
        this(null, false);
}
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());
            }
        }
 
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
复制代码

上面有几行代码是关键代码:

1. public Handler(Callback callback, boolean async)

Handler.Callback是用来处理Message的一种手段,若是没有传递该参数,那么就应该重写Handler的handleMessage方法,也就是说为了使得Handler可以处理Message,咱们有两种办法:

(1)向Hanlder的构造函数传入一个Handler.Callback对象,并实现Handler.Callback的handleMessage方法。

(2)无需向Hanlder的构造函数传入Handler.Callback对象,可是须要重写Handler自己的handleMessage方法。

//在主线程中建立mHandler,因此自动绑定主线程
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what){
                case 1:
                    System.out.println("handleMessage thread id " + Thread.currentThread().getId());
                    System.out.println("msg.arg1:" + msg.arg1);
                    System.out.println("msg.arg2:" + msg.arg2);
                    System.out.println("msg.obj:" + msg.obj.toString());
                    System.out.println("msg.setDate:" + msg.getData().get("QQ"));

                    textview.setText("success");
                    break;
            }
        }
    };

   private Handler mHandler2 = new Handler(new Handler.Callback() {
       @Override
       public boolean handleMessage(@NonNull Message msg) {
           switch (msg.what){
               case 1:
                   System.out.println("handleMessage thread id " + Thread.currentThread().getId());
                   System.out.println("msg.arg1:" + msg.arg1);
                   System.out.println("msg.arg2:" + msg.arg2);
                   System.out.println("msg.obj:" + msg.obj.toString());
                   System.out.println("msg.setDate:" + msg.getData().get("QQ"));

                   textview.setText("success");
                   break;
           }
           return false;
       }
   });
复制代码

也就是说不管哪一种方式,咱们都得经过某种方式实现handleMessage方法,这点与Java中对Thread的设计有殊途同归之处。

在Java中,若是咱们想使用多线程,有两种办法:

(1)向Thread的构造函数传入一个Runnable对象,并实现Runnable的run方法。

(2)无需向Thread的构造函数传入Runnable对象,可是要重写Thread自己的run方法。

2. mLooper = Looper.myLooper();

首先经过Looper.myLooper()获取了当前线程保存的Looper实例。

3.mQueue = mLooper.mQueue;

而后再获取该Looper实例中保存的消息队列MessageQueue,这样就保证了Handler的实例与Looper实例中MessageQueue关联上了。

(二)sendMessage()方法

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
复制代码
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
复制代码
public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
复制代码
public boolean sendMessageAtTime(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);
    }
复制代码

展转反则最后调用了sendMessageAtTime,在此方法内部有直接获取MessageQueue,而后调用了enqueueMessage方法,咱们再来看看此方法:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
复制代码

上面有几行代码是关键代码:

1. msg.target = this;

该代码将Message的target绑定为当前的Handler。

2. queue.enqueueMessage;

变量queue表示的是Handler所绑定的消息队列MessageQueue,经过调用queue.enqueueMessage(msg, uptimeMillis)咱们将Message放入到消息队列中。

如今已经很清楚了Looper会调用prepare()和loop()方法,在当前执行的线程中保存一个Looper实例,这个实例会保存一个MessageQueue对象,而后当前线程进入一个无限循环中去,不断从MessageQueue中读取Handler发来的消息。而后再回调建立该消息的Handler中的dispathMessage方法。Handler的dispatchMessage的源码以下:

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

上面有几行代码是关键代码:

1. if (msg.callback != null) { handleCallback(msg); }

首先会判断msg.callback存不存在,msg.callback是Runnable类型,若是msg.callback存在,那么说明该Message是经过执行Handler的postXXX系列方法将Message放入到消息队列中的,这种状况下会执行handleCallback(msg), handleCallback源码以下:

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

这样咱们咱们就清楚地看到咱们执行了msg.callback的run方法,也就是执行了postXXX所传递的Runnable对象的run方法。

2. else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } }

若是咱们不是经过postXXX系列方法将Message放入到消息队列中的,那么msg.callback就是null,代码继续往下执行,接着咱们会判断Handler的成员字段mCallback存不存在。mCallback是Hanlder.Callback类型的,咱们在上面的Handler构造函数提到过,在Handler的构造函数中咱们能够传递Hanlder.Callback类型的对象,该对象须要实现handleMessage方法,若是咱们在构造函数中传递了该Callback对象,那么咱们就会让Callback的handleMessage方法来处理Message。

3.handleMessage(msg);

若是咱们在构造函数中没有传入Callback类型的对象,那么mCallback就为null,那么咱们会调用Handler自身的hanldeMessage方法,该方法默认是个空方法,咱们须要本身是重写实现该方法。

综上,咱们能够看到Handler提供了三种途径处理Message,并且处理有先后优先级之分:首先尝试让postXXX中传递的Runnable执行,其次尝试让Handler构造函数中传入的Callback的handleMessage方法处理,最后才是让Handler自身的handleMessage方法处理Message。

让咱们看一下handleMessage(msg)

/**
    * Subclasses must implement this to receive messages.
    */
   public void handleMessage(Message msg) {
   }
复制代码

能够看到这是一个空方法,为何呢,由于消息的最终回调是由咱们控制的,咱们在建立handler的时候都是复写handleMessage方法,而后根据msg.what进行消息处理。

private Handler mHandler = new Handler(){
		public void handleMessage(android.os.Message msg){
			switch (msg.what){
			case value:
			    break;
			default:
			     break;
			}   
		};
	};
复制代码

到此,sendMessage方式流程已经解释完毕,接下来看下post方式。

(三)post()方法

mHandler.post(new Runnable()
		{
			@Override
			public void run()
			{
				Log.e("TAG", Thread.currentThread().getName());
				mTxt.setText("yoxi");
			}
		});
复制代码

而后run方法中能够写更新UI的代码,其实这个Runnable并无建立什么线程,而是发送了一条消息,下面看源码:

public final boolean post(Runnable r){
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
复制代码
private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
复制代码

能够看到,在getPostMessage中,获得了一个Message对象,而后将咱们建立的Runable对象做为callback属性,赋值给了此message。 注:产生一个Message对象,能够new也可使用Message.obtain()方法;二者均可以,可是更建议使用obtain方法,由于Message内部维护了一个Message池用于Message的复用,避免使用new从新分配内存。

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
复制代码
public boolean sendMessageAtTime(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);
    }
复制代码

最终和handler.sendMessage同样,调用了sendMessageAtTime,而后调用了enqueueMessage方法,给msg.target赋值为handler,最终加入MessagQueue。

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

能够看到,这里msg的callback和target都有值,那么会执行msg.callback != null的判断,则执行handleCallback回调,也就是咱们的Runnable对象。

3、总结

到此,这个流程已经解释完毕,让咱们首先总结一下:

  1. 首先Looper.prepare()在本线程中保存一个Looper实例,而后该实例中保存一个MessageQueue对象;由于Looper.prepare()在一个线程中只能调用一次,因此MessageQueue在一个线程中只会存在一个。

  2. 在Lopper构造函数中,建立了一个消息队列MessageQueue,并将其赋值给其成员字段mQueue,这样Looper也就与MessageQueue经过成员字段mQueue进行了关联。

  3. Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,而后回调msg.target.dispatchMessage(msg)方法。

  4. Handler的构造方法,会首先获得当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。

  5. Handler的sendMessage方法,会给msg的target赋值为handler自身,而后加入MessageQueue中。

  6. 在构造Handler实例时,咱们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。

4、一图胜千言

咱们在本文讨论了Thread、MessageQueue、Looper以及Hanlder的之间的关系,咱们能够经过以下一张传送带的图来更形象的理解他们之间的关系。

在现实生活的生产生活中,存在着各类各样的传送带,传送带上面洒满了各类货物,传送带在发动机滚轮的带动下一直在向前滚动,不断有新的货物放置在传送带的一端,货物在传送带的带动下送到另外一端进行收集处理。

咱们能够把传送带上的货物看作是一个个的Message,而承载这些货物的传送带就是装载Message的消息队列MessageQueue。传送带是靠发送机滚轮带动起来转动的,咱们能够把发送机滚轮看作是Looper,而发动机的转动是须要电源的,咱们能够把电源看作是线程Thread,全部的消息循环的一切操做都是基于某个线程的。一切准备就绪,咱们只须要按下电源开关发动机就会转动起来,这个开关就是Looper的loop方法,当咱们按下开关的时候,咱们就至关于执行了Looper的loop方法,此时Looper就会驱动着消息队列循环起来。

那Hanlder在传送带模型中至关于什么呢?咱们能够将Handler看作是放入货物以及取走货物的管道:货物从一端顺着管道划入传送带,货物又从另外一端顺着管道划出传送带。咱们在传送带的一端放入货物的操做就至关于咱们调用了Handler的sendMessageXXX、sendEmptyMessageXXX或postXXX方法,这就把Message对象放入到了消息队列MessageQueue中了。当货物从传送带的另外一端顺着管道划出时,咱们就至关于调用了Hanlder的dispatchMessage方法,最终执行handleMessage方法,在该方法中咱们完成对Message的处理。

特别感谢:

Android 异步消息处理机制 让你深刻理解 Looper、Handler、Message三者关系

深刻源码解析Android中的Handler,Message,MessageQueue,Looper

相关文章
相关标签/搜索