一、概述

Handler是Android中解决异步音讯的机制。Looper、Handler、MessageQueue、Message概括来说就是:Looper负责的就是创立一个MessageQueue,而后进入一个有限循环体一直从该MessageQueue中读取音讯Message,而后回调相应的音讯处理函数,而音讯的创建者就是一个或多个Handler,执行实现一个音讯后则持续循环。

二、MessageQueue详解

音讯队列MessageQueue就是寄存音讯的队列。那队列中存储的音讯是什么呢?假如咱们在UI界面上单击了某个按钮,而此时程序又恰好收到了某个播送事件,那咱们如何解决这两件事呢? 因为一个线程在某一时刻只能解决一件事件,不能同时解决多件事件,所以咱们不能同时解决按钮的单击事件和播送事件,咱们只能挨个对其进行解决,只有挨个解决就要有解决的先后顺序。为此Android把UI界面上单击按钮的事件封装成了一个Message,将其放入到MessageQueue外面去,行将单击按钮事件的Message入栈到音讯队列中,而后再将播送事件的封装成以Message,也将其入栈到音讯队列中。也就是说一个Message对象示意的是线程须要解决的一件事件,音讯队列就是一堆须要解决的Message的池。线程Thread会顺次取出音讯队列中的音讯,顺次对其进行解决。

MessageQueue中有两个比拟重要的办法,一个是enqueueMessage办法,一个是next办法。enqueueMessage办法用于将一个Message放入到音讯队列MessageQueue中,next办法是从音讯队列MessageQueue中阻塞式地取出一个Message。

三、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实例。

(二)构造函数

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

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

下面有几行代码是要害代码:

1. final Looper me = myLooper();

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去解决。

二、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对象。

三、总结

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

  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)最终调用的办法。

四、一图胜千言

咱们在本文探讨了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 handle面试】MessageQueue如何放弃各线程增加音讯的线程平安?
【Android handle面试】子线程中的looper在无音讯的时候应该怎么解决缩小性能问题?
【Android handle面试】一个线程有几个hander和几个looper?

本文转自 https://juejin.cn/post/6844904195653386248,如有侵权,请分割删除。