Handler 源码分析 - Java 层
<p>Handler最常见的使用场景就是下载回调,为了不影响用户体验Android不支持在主线程中进行耗时时操作,长时间的耗时操作会产生ANR异常,而下载无疑是耗时操作,所以我们会在子线程中进行下载。但,下载完毕进行UI操作却会发生异常,原来谷歌为了不让UI的操作出现冲突(线程的不可确定性),所以规定只能在子线程中进行UI操作,可这就尴尬了...即不让在主线程中进行联网操作,又不让在子线程中进行UI操作,我们如何将告诉主线程我们已经下载完毕了呢?这时就要用到Handler了.</p> <p>Handler的简单使用</p> <p><img src="https://simg.open-open.com/show/587251e02c2a17caa9907005f9e0e4f7.gif"></p> <p>test</p> <p>下面是最简单的使用.</p> <pre> <code class="language-java">// 1.创建Handler对象, 重写方法. mTestHandler = new TestHandler(this); -----------------------------点击模拟下载--------------------------------- public void download(View view) { text.setText("开始下载..."); new Thread(){ @Override public void run() { // 2.创建消息对象 Message msg = mTestHandler.obtainMessage(); msg.obj = "下载完毕"; msg.arg1 = 1; // 3.发送延时消息 mTestHandler.sendMessageDelayed(msg, 2000); } }.start(); } ---------------------------创建一个类继承Handler-------------------------------- // * 用软引用的方法持有Activity对象防止内存泄露 private WeakReference<MainActivity> activity ; public TestHandler(MainActivity activity){ this.activity = new WeakReference<>(activity); } // 回调方法. @Override public void handleMessage(Message msg) { switch (msg.arg1){ case 1: activity.get().text.setText((String) msg.obj); break; } }</code></pre> <p>继承 <strong>Handler</strong> 对象并重写 handleMessage() ,然后创建 <strong>Handler</strong> 对象,调用 obtainMessagee() 方法获取 <strong>Message</strong> 对象,将数据赋予 <strong>Message</strong> ,并发送出去,而发送的消息会回调给 <strong>Handler</strong> 的 handleMessage() 方法.</p> <p>在子线程中创建Handler对象.</p> <p>刚才我们在主线程中创建了 <strong>Handler</strong> 对象,在子线程中调用 <strong>Handler</strong> 的 sendMessageDelayed() 方法将 <strong>Message</strong> 带到主线程间完成了线程中的通信,那我们能在子线程中创建 <strong>Handler</strong> 吗 ? 答案是不可以。在子线程中创建 <strong>Handler</strong> 对象会抛出如下异常:</p> <pre> <code class="language-java">Can't create handler inside thread that has not called Looper.prepare()</code></pre> <p>异常说的很明白它需要调用 Looper.prepare() 。我们来看看源码,为什么在主线和中可以创建 <strong>Handler</strong> 对象呢?点击 <strong>new Handler()</strong> ,在两个参数的构造方法中我们发现了刚才抛出异常的代码.</p> <pre> <code class="language-java">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;</code></pre> <p>可以看到异常抛出的原因是因为mLooper这个对象为null,我们来看看myLooper里面做了什么?</p> <p>-----------可我们并没有在主线程中调用这个方法啊?那是因为该方法在 <strong>ActivityThread</strong> 类里调用了,我们这里暂时不看它. 继续查看 Looper.myLooper() 里面的内容.</p> <pre> <code class="language-java">public static @Nullable Looper myLooper() { return sThreadLocal.get(); }</code></pre> <p>myLooper()实现调用了 <strong>ThreadLocal</strong> 身上的 get() 方法,它返回一个 <strong>Looper</strong> ,既然有 get() 必然在一个地方 set() 了,于是在 <strong>Looper</strong> 中发现以下代码</p> <pre> <code class="language-java">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)); }</code></pre> <p>ThreadLocal</p> <p>我们把 <strong>Looper</strong> 对象存放在了 <strong>ThreadLocal</strong> 中,当需要 <strong>Looper</strong> 时也是从 <strong>ThreadLocal</strong> 中取的,为什么要将 <strong>Looper</strong> 存放在 <strong>ThreadLocal</strong> 中呢?下面是它的 get() 方法.</p> <pre> <code class="language-java">public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); }</code></pre> <p>ThreadLocal内部有一个 <strong>Map</strong> 集合,根据它所在的线程名取 <strong>Looper</strong> ,而 <strong>ThreadLocal</strong> 是在创建 <strong>Looper</strong> 时创建的所以 <strong>Looper</strong> 与 <strong>ThreadLocal</strong> 是同一线程的,而我们创建handler时会调用 myLooper() 方法,该方法调用 get() 方法时会根据handler所有线程名取 <strong>Looper</strong> 对象,所以我们要保证 Looper.prepare() 与 <strong>Handler</strong> 在同一线程中.</p> <p>现在让我们来理一下思路,首先创建 <strong>Handler</strong> 需要 <strong>Looper</strong> ,而 <strong>Looper</strong> 是在 prepare() 方法中创建的,也就是说如果我们想在子线程中创建 <strong>Handler</strong> 只需要在之前调用</p> <p><img src="https://simg.open-open.com/show/117f0fa68a3305e7d90bcbc691e400a0.gif"></p> <p>test</p> <pre> <code class="language-java">`` public void run() { Looper.prepare(); mTestHandler = new Handler(){ @Override public void handleMessage(Message msg) { Log.d("MainActivity", Thread.currentThread().getName()); } }; mTestHandler.sendMessage(mTestHandler.obtainMessage()); }</code></pre> <p>MesageQuene</p> <p>可是问题又来了,虽然没有抛出异常, handleMessage() 方法却一直接收不到消息.既然是收不到消息,那我们来看看Handler内部是怎么发送消息的吧,我们继续查看 sendMessage() 方法.</p> <pre> <code class="language-java">// 这里我们看到了前面的发送延时消息方法,延时+当时时间 并调用下一方法 sendMessage(msg) -> sendMessageDelayed(msg, 0) -> sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis) ---------------------------------------------------------------------- public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { ... return false; } return enqueueMessage(queue, msg, uptimeMillis); } ---------------------------------------------------------------------- private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; // this = handler; 把消息与handler进行绑定. if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }</code></pre> <p>最后调用了 <strong>MesageQuene</strong> 的 enqueueMessage() 方法,即入队列,那 <strong>MesageQuene</strong> 对象是在什么时候创建的呢?是在创建 <strong>Looper</strong> 对象时创建的。</p> <pre> <code class="language-java">private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }</code></pre> <p>继续来看 <strong>MesageQuene</strong> 的 enqueueMessage() 方法.</p> <pre> <code class="language-java">... Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; ...</code></pre> <p>enqueueMessage() 就是入队列的意思, <strong>Message</strong> 是单链表结构,它持有下一个 <strong>Message</strong> 对象的引用,因为 <strong>MesageQuene</strong> 中可能存有多个未处理的消息,所以需要判断 <strong>MesageQuene</strong> 中有多少消息,若有多个则将当前消息放置在最后,可以看成集合的add方法.</p> <p>Looper</p> <p>sendMessage()方法只是将消息加入消息队列中,那消息是如何取出并发给handler的呢?我们来看下 Looper.loop()</p> <pre> <code class="language-java">... for (;;) { Message msg = queue.next(); // might block ... try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } ...</code></pre> <p>这里的target就是handler(在handler的 enqueueMessage() 方法里,我们将消息与handler进行了绑定),所以调用了handler身上的 dispatchMessage() 方法,而该方法最终又调用了 handleMessage() 方法,并将消息传递进去.</p> <pre> <code class="language-java">public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }</code></pre> <p>上面就是handler发送消息的大致流程最后我们总结一下.</p> <p>总结:创建 <strong>Handler</strong> 对象时会调用 Looper.prepare() 方法,该方法会用从 <strong>ThreadLocal</strong> 对象中取出looper, 所以如果 <strong>ThreadLocal</strong> 中没有先存 <strong>Looper</strong> 或不在同一线程中则取不到对象, <strong>Handler</strong> 就会抛出异常.</p> <ul> <li>问题1.:主线程在哪里调用了 prepare() 方法的?见底部代码.</li> </ul> <p>用handler发送消息方法时,会调用到 <strong>MesageQuene</strong> 的方法, 而mQuene是在创建 <strong>Looper</strong> 对象时创建的, <strong>Looper</strong> 对象是在调用 prepare() 方法时创建的, 也就是说mQuene与 <strong>Looper</strong> 是在同一线程.</p> <p>我们发送消息时会将所发送的消息加入消息队列,而后调用Looper.loop()方法才能将消息取出并传送给handler,如果不调用Looper.loop()则消息无法取出</p> <ul> <li>问题2:为什么在主线中创建可以接收消息?见底部代码.</li> </ul> <p>最后在子线程中创建handler那handleMessage是在哪个线程回调的呢?</p> <p>这个问题在上面就已经说了,handleMessage在loop()方法调用,而loop()与Looper是在同一线程,也就是说最终会在子线程回调.那如何让回调在主线程呢?调用prepareMainLooper()方法,而不是prepare()就可让回调在主线程中运行.</p> <p>下面代码很好回答了第1和第2问题.</p> <pre> <code class="language-java">public static final void main(String[] args) { .... // 1.主线程创建Looper Looper.prepareMainLooper(); if (sMainThreadHandler == null) { sMainThreadHandler = new Handler(); } ActivityThread thread = new ActivityThread(); thread.attach(false); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); ...</code></pre> <p> </p> <p>来自:http://www.jianshu.com/p/1bd6e015653f</p> <p> </p>
本文由用户 icya3508 自行上传分享,仅供网友学习交流。所有权归原作者,若您的权利被侵害,请联系管理员。
转载本站原创文章,请注明出处,并保留原始链接、图片水印。
本站是一个以用户分享为主的开源技术平台,欢迎各类分享!