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>
在该方法中有两件事需要注意:
-
msg.target = this
该代码将Message的target绑定为当前的Handler
- 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中。