Handler,Looper,Message的深入理解

1、Handler的由来

  当程序第一次启动的时候,Android会同时启动一条主线程( Main Thread)来负责处理与UI相关的事件,我们叫做UI线程。

  Android的UI操作并不是线程安全的(出于性能优化考虑),意味着如果多个线程并发操作UI线程,可能导致线程安全问题。

  为了解决Android应用多线程问题—Android平台只允许UI线程修改Activity里的UI组建,就会导致新启动的线程无法改变界面组建的属性值。

  简单的说:当主线程队列处理一个消息超过5秒,android 就会抛出一个 ANP(无响应)的异常,所以,我们需要把一些要处理比较长的消息,放在一个单独线程里面处理,把处理以后的结果,返回给主线程运行,就需要用的Handler来进行线程建的通信。

2、Handler的作用

2.1 让线程延时执行

主要用到的两个方法:

  • final boolean postAtTime(Runnable r, long uptimeMillis)

  • final boolean postDelayed(Runnable r, long delayMillis)

2.2 让任务在其他线程中执行并返回结果

分为两个步骤:

  • 在新启动的线程中发送消息

      使用Handler对象的sendMessage()方法或者SendEmptyMessage()方法发送消息。

  • 在主线程中获取处理消息

      重写Handler类中处理消息的方法(void handleMessage(Message msg)),当新启动的线程发送消息时,消息发送到与之关联的MessageQueue。而Hanlder不断地从MessageQueue中获取并处理消息。

3、Handler更新UI线程一般使用

  • 首先要进行Handler 申明,复写handleMessage方法( 放在主线程中)
<code class="hljs java has-numbering"><span class="hljs-keyword">private</span> Handler handler = <span class="hljs-keyword">new</span> Handler() {

        <span class="hljs-annotation">@Override</span>
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">handleMessage</span>(Message msg) {
            <span class="hljs-comment">// TODO 接收消息并且去更新UI线程上的控件内容</span>
            <span class="hljs-keyword">if</span> (msg.what == UPDATE) {
                <span class="hljs-comment">// 更新界面上的textview</span>
                tv.setText(String.valueOf(msg.obj));
            }
            <span class="hljs-keyword">super</span>.handleMessage(msg);
        }
    };</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li></ul>
  • 子线程发送Message给ui线程表示自己任务已经执行完成,主线程可以做相应的操作了。
<code class="hljs java has-numbering"><span class="hljs-keyword">new</span> Thread() {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span>() {
                <span class="hljs-comment">// TODO 子线程中通过handler发送消息给handler接收,由handler去更新TextView的值</span>
                <span class="hljs-keyword">try</span> {
                       <span class="hljs-comment">//do something</span>

                        Message msg = <span class="hljs-keyword">new</span> Message();
                        msg.what = UPDATE;                  
                        msg.obj = <span class="hljs-string">"更新后的值"</span> ;
                        handler.sendMessage(msg);
                    }
                } <span class="hljs-keyword">catch</span> (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li></ul>
4、Handler原理分析

4.1  Handler的构造函数

① public Handler()
② public Handler(Callbackcallback)
③ public Handler(Looperlooper)
④ public Handler(Looperlooper, Callbackcallback)  

  • 第①个和第②个构造函数都没有传递Looper,这两个构造函数都将通过调用Looper.myLooper()获取当前线程绑定的Looper对象,然后将该Looper对象保存到名为mLooper的成员字段中。  
      下面来看①②个函数源码:
      
<code class="hljs java has-numbering"><span class="hljs-number">113</span>    <span class="hljs-keyword">public</span> <span class="hljs-title">Handler</span>() {
<span class="hljs-number">114</span>        <span class="hljs-keyword">this</span>(<span class="hljs-keyword">null</span>, <span class="hljs-keyword">false</span>);
<span class="hljs-number">115</span>    }

<span class="hljs-number">127</span>    <span class="hljs-keyword">public</span> <span class="hljs-title">Handler</span>(Callback callback) {
<span class="hljs-number">128</span>        <span class="hljs-keyword">this</span>(callback, <span class="hljs-keyword">false</span>);
<span class="hljs-number">129</span>    }

<span class="hljs-comment">//他们会调用Handler的内部构造方法</span>

<span class="hljs-number">188</span>    <span class="hljs-keyword">public</span> <span class="hljs-title">Handler</span>(Callback callback, <span class="hljs-keyword">boolean</span> async) {
<span class="hljs-number">189</span>        <span class="hljs-keyword">if</span> (FIND_POTENTIAL_LEAKS) {
<span class="hljs-number">190</span>      <span class="hljs-keyword">final</span> Class<? extends Handler> klass = getClass();
<span class="hljs-number">191</span>      <span class="hljs-keyword">if</span> ((klass.isAnonymousClass() ||klass.isMemberClass()
         || klass.isLocalClass()) &&
<span class="hljs-number">192</span>                    (klass.getModifiers() & Modifier.STATIC) == <span class="hljs-number">0</span>) {
<span class="hljs-number">193</span>                Log.w(TAG, <span class="hljs-string">"The following Handler class should be static or leaks might occur: "</span> +
<span class="hljs-number">194</span>                    klass.getCanonicalName());
<span class="hljs-number">195</span>            }
<span class="hljs-number">196</span>        }
<span class="hljs-number">197</span><span class="hljs-javadoc">/************************************
198        mLooper = Looper.myLooper();
199        if (mLooper == null) {
200            throw new RuntimeException(
201                "Can't create handler inside thread that has not called Looper.prepare()");
202        }
203        mQueue = mLooper.mQueue;
204        mCallback = callback;
205        mAsynchronous = async;
206    }
</span></code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li></ul>

  我们看到暗红色的重点部分:

  通过Looper.myLooper()获取了当前线程保存的Looper实例,又通过这个Looper实例获取了其中保存的MessageQueue(消息队列)。每个Handler 对应一个Looper对象,产生一个MessageQueue
  

  • 第③个和第④个构造函数传递了Looper对象,这两个构造函数会将该Looper保存到名为mLooper的成员字段中。
      下面来看③④个函数源码:
<code class="hljs cs has-numbering"><span class="hljs-number">136</span>    <span class="hljs-keyword">public</span> <span class="hljs-title">Handler</span>(Looper looper) {
<span class="hljs-number">137</span>        <span class="hljs-keyword">this</span>(looper, <span class="hljs-keyword">null</span>, <span class="hljs-keyword">false</span>);
<span class="hljs-number">138</span>    } 

<span class="hljs-number">147</span>    <span class="hljs-keyword">public</span> <span class="hljs-title">Handler</span>(Looper looper, Callback callback) {
<span class="hljs-number">148</span>        <span class="hljs-keyword">this</span>(looper, callback, <span class="hljs-keyword">false</span>);
<span class="hljs-number">149</span>    }
<span class="hljs-comment">//他们会调用Handler的内部构造方法</span>

<span class="hljs-number">227</span>    <span class="hljs-keyword">public</span> <span class="hljs-title">Handler</span>(Looper looper, Callback callback, boolean <span class="hljs-keyword">async</span>) {
<span class="hljs-number">228</span>        mLooper = looper;
<span class="hljs-number">229</span>        mQueue = looper.mQueue;
<span class="hljs-number">230</span>        mCallback = callback;
<span class="hljs-number">231</span>        mAsynchronous = <span class="hljs-keyword">async</span>;
<span class="hljs-number">232</span>    }
</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul>
  • 第②个和第④个构造函数还传递了Callback对象,Callback是Handler中的内部接口,需要实现其内部的handleMessage方法,Callback代码如下:
<code class="hljs cs has-numbering"><span class="hljs-number">80</span>     <span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> Callback {
<span class="hljs-number">81</span>         <span class="hljs-keyword">public</span> boolean More ...<span class="hljs-title">handleMessage</span>(Message msg);
<span class="hljs-number">82</span>     }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul>

  Handler.Callback是用来处理Message的一种手段,如果没有传递该参数,那么就应该重写Handler的handleMessage方法,也就是说为了使得Handler能够处理Message,我们有两种办法:
  
 1. 向Hanlder的构造函数传入一个Handler.Callback对象,并实现Handler.Callback的handleMessage方法  
  
 2. 无需向Hanlder的构造函数传入Handler.Callback对象,但是需要重写Handler本身的handleMessage方法  
   
   也就是说无论哪种方式,我们都得通过某种方式实现handleMessage方法,这点与Java中对Thread的设计有异曲同工之处。

4.2 Handle发送消息的几个方法源码

<code class="hljs java has-numbering">   <span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">sendMessage</span>(Message msg)
    {
        <span class="hljs-keyword">return</span> sendMessageDelayed(msg, <span class="hljs-number">0</span>);
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li></ul>
<code class="hljs java has-numbering">   <span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">sendEmptyMessageDelayed</span>(<span class="hljs-keyword">int</span> what, <span class="hljs-keyword">long</span> delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        <span class="hljs-keyword">return</span> sendMessageDelayed(msg, delayMillis);
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>
<code class="hljs java has-numbering"> <span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">sendMessageDelayed</span>(Message msg, <span class="hljs-keyword">long</span> delayMillis)
    {
        <span class="hljs-keyword">if</span> (delayMillis < <span class="hljs-number">0</span>) {
            delayMillis = <span class="hljs-number">0</span>;
        }
        <span class="hljs-keyword">return</span> sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li></ul>
<code class="hljs java has-numbering"> <span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">sendMessageAtTime</span>(Message msg, <span class="hljs-keyword">long</span> uptimeMillis) {
        MessageQueue queue = mQueue;
        <span class="hljs-keyword">if</span> (queue == <span class="hljs-keyword">null</span>) {
            RuntimeException e = <span class="hljs-keyword">new</span> RuntimeException(
                    <span class="hljs-keyword">this</span> + <span class="hljs-string">" sendMessageAtTime() called with no mQueue"</span>);
            Log.w(<span class="hljs-string">"Looper"</span>, e.getMessage(), e);
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
        }
        <span class="hljs-keyword">return</span> enqueueMessage(queue, msg, uptimeMillis);
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li></ul>

我们可以看出他们最后都调用了sendMessageAtTime(),然后返回了enqueueMessage方法,下面看一下此方法源码:

<code class="hljs java has-numbering"><span class="hljs-number">626</span>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">enqueueMessage</span>(MessageQueue queue, Message msg, <span class="hljs-keyword">long</span> uptimeMillis) {
      <span class="hljs-comment">//把当前的handler作为msg的target属性</span>
<span class="hljs-number">627</span>        msg.target = <span class="hljs-keyword">this</span>;
<span class="hljs-number">628</span>        <span class="hljs-keyword">if</span> (mAsynchronous) {
<span class="hljs-number">629</span>            msg.setAsynchronous(<span class="hljs-keyword">true</span>);
<span class="hljs-number">630</span>        }
<span class="hljs-number">631</span>        <span class="hljs-keyword">return</span> queue.enqueueMessage(msg, uptimeMillis);
<span class="hljs-number">632</span>    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li></ul>

在该方法中有两件事需要注意:

  1. msg.target = this

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

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

过下图可以看到完整的方法调用顺序:

这里写图片描述 

5、Looper原理分析

  我们一般在主线程申明Handler,有时我们需要继承Thread类实现自己的线程功能,当我们在里面申明Handler的时候会报错。其原因是主线程中已经实现了两个重要的Looper方法,下面看一看ActivityThread.java中main方法的源码:

<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) {
            <span class="hljs-comment">//......省略</span>
<span class="hljs-number">5205</span>        Looper.prepareMainLooper();<span class="hljs-comment">//></span>
<span class="hljs-number">5206</span>
<span class="hljs-number">5207</span>        ActivityThread thread = <span class="hljs-keyword">new</span> ActivityThread();
<span class="hljs-number">5208</span>        thread.attach(<span class="hljs-keyword">false</span>);
<span class="hljs-number">5209</span>
<span class="hljs-number">5210</span>        <span class="hljs-keyword">if</span> (sMainThreadHandler == <span class="hljs-keyword">null</span>) {
<span class="hljs-number">5211</span>            sMainThreadHandler = thread.getHandler();
<span class="hljs-number">5212</span>        }
<span class="hljs-number">5213</span>
<span class="hljs-number">5214</span>        AsyncTask.init();
<span class="hljs-number">5215</span>
<span class="hljs-number">5216</span>        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">false</span>) {
<span class="hljs-number">5217</span>            Looper.myLooper().setMessageLogging(<span class="hljs-keyword">new</span>
<span class="hljs-number">5218</span>   LogPrinter(Log.DEBUG, <span class="hljs-string">"ActivityThread"</span>));
<span class="hljs-number">5219</span>        }
<span class="hljs-number">5220</span>
<span class="hljs-number">5221</span>        Looper.loop();<span class="hljs-comment">//></span>
<span class="hljs-number">5222</span>
<span class="hljs-number">5223</span>        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(<span class="hljs-string">"Main thread loop unexpectedly exited"</span>);
<span class="hljs-number">5224</span>    }
<span class="hljs-number">5225</span>}</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li></ul>

5.1 首先看prepare()方法

<code class="hljs cs has-numbering"><span class="hljs-number">70</span>     <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">prepare</span>() {
<span class="hljs-number">71</span>         prepare(<span class="hljs-keyword">true</span>);
<span class="hljs-number">72</span>     }
<span class="hljs-number">73</span> 
<span class="hljs-number">74</span>     <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">prepare</span>(boolean quitAllowed) {
     <span class="hljs-comment">//证了一个线程中只有一个Looper实例</span>
<span class="hljs-number">75</span>         <span class="hljs-keyword">if</span> (sThreadLocal.<span class="hljs-keyword">get</span>() != <span class="hljs-keyword">null</span>) {
<span class="hljs-number">76</span>             <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(<span class="hljs-string">"Only one Looper may be created per thread"</span>);
<span class="hljs-number">77</span>         }
<span class="hljs-number">78</span>         sThreadLocal.<span class="hljs-keyword">set</span>(<span class="hljs-keyword">new</span> Looper(quitAllowed));
<span class="hljs-number">79</span>     }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li></ul>

该方法会调用Looper构造函数同时实例化出MessageQueue和当前thread.

<code class="hljs java has-numbering"><span class="hljs-number">186</span>    <span class="hljs-keyword">private</span> <span class="hljs-title">Looper</span>(<span class="hljs-keyword">boolean</span> quitAllowed) {
<span class="hljs-number">187</span>        mQueue = <span class="hljs-keyword">new</span> MessageQueue(quitAllowed);
<span class="hljs-number">188</span>        mThread = Thread.currentThread();
<span class="hljs-number">189</span>    } 

<span class="hljs-number">182</span>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> MessageQueue <span class="hljs-title">myQueue</span>() {
<span class="hljs-number">183</span>        <span class="hljs-keyword">return</span> myLooper().mQueue;
<span class="hljs-number">184</span>    }
</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul>

  prepare()方法中通过ThreadLocal对象实现Looper实例与线程的绑定。(不清楚的可以查看 ThreadLocal的使用规则和源码分析) 

5.2  loop()方法

<code class="hljs java has-numbering"><span class="hljs-number">109</span>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">loop</span>() {
<span class="hljs-number">110</span>        <span class="hljs-keyword">final</span> Looper me = myLooper();
<span class="hljs-number">111</span>        <span class="hljs-keyword">if</span> (me == <span class="hljs-keyword">null</span>) {
<span class="hljs-number">112</span>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(<span class="hljs-string">"No Looper; Looper.prepare() wasn't called on this thread."</span>);
<span class="hljs-number">113</span>        }
<span class="hljs-number">114</span>        <span class="hljs-keyword">final</span> MessageQueue queue = me.mQueue;
<span class="hljs-number">115</span>
<span class="hljs-number">118</span>        Binder.clearCallingIdentity();
<span class="hljs-number">119</span>        <span class="hljs-keyword">final</span> <span class="hljs-keyword">long</span> ident = Binder.clearCallingIdentity();
<span class="hljs-number">120</span>
<span class="hljs-number">121</span>        <span class="hljs-keyword">for</span> (;;) {
<span class="hljs-number">122</span>            Message msg = queue.next(); <span class="hljs-comment">// might block</span>
<span class="hljs-number">123</span>            <span class="hljs-keyword">if</span> (msg == <span class="hljs-keyword">null</span>) {
<span class="hljs-number">124</span>               
<span class="hljs-number">125</span>                <span class="hljs-keyword">return</span>;
<span class="hljs-number">126</span>            }
<span class="hljs-number">127</span>
<span class="hljs-number">129</span>            Printer logging = me.mLogging;
<span class="hljs-number">130</span>            <span class="hljs-keyword">if</span> (logging != <span class="hljs-keyword">null</span>) {
<span class="hljs-number">131</span>                logging.println(<span class="hljs-string">">>>>> Dispatching to "</span> + msg.target + <span class="hljs-string">" "</span> +
<span class="hljs-number">132</span>                        msg.callback + <span class="hljs-string">": "</span> + msg.what);
<span class="hljs-number">133</span>            }
<span class="hljs-comment">//重点****</span>
<span class="hljs-number">135</span>            msg.target.dispatchMessage(msg);
<span class="hljs-number">136</span>
<span class="hljs-number">137</span>            <span class="hljs-keyword">if</span> (logging != <span class="hljs-keyword">null</span>) {
<span class="hljs-number">138</span>                logging.println(<span class="hljs-string">"<<<<< Finished to "</span> + msg.target + <span class="hljs-string">" "</span> + msg.callback);
<span class="hljs-number">139</span>            }
<span class="hljs-number">140</span>
<span class="hljs-number">142</span>            <span class="hljs-comment">// identity of the thread wasn't corrupted.</span>
<span class="hljs-number">143</span>            <span class="hljs-keyword">final</span> <span class="hljs-keyword">long</span> newIdent = Binder.clearCallingIdentity();
<span class="hljs-number">144</span>            <span class="hljs-keyword">if</span> (ident != newIdent) {
<span class="hljs-number">145</span>                Log.wtf(TAG, <span class="hljs-string">"Thread identity changed from 0x"</span>
<span class="hljs-number">146</span>                        + Long.toHexString(ident) + <span class="hljs-string">" to 0x"</span>
<span class="hljs-number">147</span>                        + Long.toHexString(newIdent) + <span class="hljs-string">" while dispatching to "</span>
<span class="hljs-number">148</span>                        + msg.target.getClass().getName() + <span class="hljs-string">" "</span>
<span class="hljs-number">149</span>                        + msg.callback + <span class="hljs-string">" what="</span> + msg.what);
<span class="hljs-number">150</span>            }
<span class="hljs-number">151</span>
<span class="hljs-number">152</span>            msg.recycleUnchecked();
<span class="hljs-number">153</span>        }
<span class="hljs-number">154</span>    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li></ul>

  首先looper对象不能为空,就是说loop()方法调用必须在prepare()方法的后面。

 Looper一直在不断的从消息队列中通过MessageQueue的next方法获取Message,然后通过代码msg.target.dispatchMessage(msg)让该msg所绑定的Handler(Message.target)执行dispatchMessage方法以实现对Message的处理。

Handler的dispatchMessage的源码如下:

<code class="hljs cs has-numbering"><span class="hljs-number">93</span>     <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">dispatchMessage</span>(Message msg) {
<span class="hljs-number">94</span>         <span class="hljs-keyword">if</span> (msg.callback != <span class="hljs-keyword">null</span>) {
<span class="hljs-number">95</span>             handleCallback(msg);
<span class="hljs-number">96</span>         } <span class="hljs-keyword">else</span> {
<span class="hljs-number">97</span>             <span class="hljs-keyword">if</span> (mCallback != <span class="hljs-keyword">null</span>) {
<span class="hljs-number">98</span>                 <span class="hljs-keyword">if</span> (mCallback.handleMessage(msg)) {
<span class="hljs-number">99</span>                     <span class="hljs-keyword">return</span>;
<span class="hljs-number">100</span>                }
<span class="hljs-number">101</span>            }
<span class="hljs-number">102</span>            handleMessage(msg);
<span class="hljs-number">103</span>        }
<span class="hljs-number">104</span>    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li></ul>

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

6、如何在子线程中使用Handler

  Handler本质是从当前的线程中获取到Looper来监听和操作MessageQueue,当其他线程执行完成后回调当前线程。

  子线程需要先prepare()才能获取到Looper的,是因为在子线程只是一个普通的线程,其ThreadLoacl中没有设置过Looper,所以会抛出异常,而在Looper的prepare()方法中sThreadLocal.set(new Looper())是设置了Looper的。

6.1 实例代码

 定义一个类实现Runnable接口或继承Thread类(一般不继承)。

<code class="hljs axapta has-numbering">    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Rub</span> <span class="hljs-inheritance"><span class="hljs-keyword">implements</span></span> <span class="hljs-title">Runnable</span> {</span>  

        <span class="hljs-keyword">public</span> Handler myHandler;  
        <span class="hljs-comment">// 实现Runnable接口的线程体 </span>
        @Override  
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> run() {  

         <span class="hljs-comment">/*①、调用Looper的prepare()方法为当前线程创建Looper对象并,
          创建Looper对象时,它的构造器会自动的创建相对应的MessageQueue*/</span>
            Looper.prepare();  

            <span class="hljs-comment">/*.②、创建Handler子类的实例,重写HandleMessage()方法,该方法处理除当前线程以外线程的消息*/</span>
             myHandler = <span class="hljs-keyword">new</span> Handler() {  
                @Override  
                <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> handleMessage(Message msg) {  
                    String ms = <span class="hljs-string">""</span>;  
                    <span class="hljs-keyword">if</span> (msg.what == <span class="hljs-number">0x777</span>) {  

                    }  
                }  

            };  
            <span class="hljs-comment">//③、调用Looper的loop()方法来启动Looper让消息队列转动起来</span>
            Looper.loop();  
        }
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li></ul><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li></ul>

注意分成三步: 

1.调用Looper的prepare()方法为当前线程创建Looper对象,创建Looper对象时,它的构造器会创建与之配套的MessageQueue。  

2.有了Looper之后,创建Handler子类实例,重写HanderMessage()方法,该方法负责处理来自于其他线程的消息。  

3.调用Looper的looper()方法启动Looper。

  然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在你创建Handler实例的线程中运行。

7、总结

Handler
      发送消息,它能把消息发送给Looper管理的MessageQueue。
      处理消息,并负责处理Looper分给它的消息。
Message
      Handler接收和处理的消息对象。
Looper
      每个线程只有一个Looper,它负责管理对应的MessageQueue,会不断地从MessageQueue取出消息,并将消息分给对应的Hanlder处理。  
      
      主线程中,系统已经初始化了一个Looper对象,因此可以直接创建Handler即可,就可以通过Handler来发送消息、处理消息。 程序自己启动的子线程,程序必须自己创建一个Looper对象,并启动它,调用Looper.prepare()方法。

prapare()方法:保证每个线程最多只有一个Looper对象。  

looper()方法:启动Looper,使用一个死循环不断取出MessageQueue中的消息,并将取出的消息分给对应的Handler进行处理。  

MessageQueue:由Looper负责管理,它采用先进先出的方式来管理Message。 

  Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。 
  
  Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值