首页 > 代码库 > Handler详解系列(一)——Handler异步消息机制详解(附图)
Handler详解系列(一)——Handler异步消息机制详解(附图)
MainActivity如下:
package cc.cn; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; import android.app.Activity; /** * Demo描述: * Android异步消息机制分析(附图) * * ======================================================= * * 问题的引入: * 在子线程中直接调用Handler handler=new Handler()此时报错: * Can't create handler inside thread that has not called Looper.prepare(). * 既然是在调用Handler的构造方法时报的错那就去看该构造方法的源码. * public Handler() { * 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 = null; * } * * 从以上代码mLooper = Looper.myLooper()可以看到当mLooper==null时就会报错 * Can't create handler inside thread that has not called Looper.prepare(). * * * 继续看Looper.myLooper()的源码. * *Return the Looper object associated with the current thread. * *Returns null if the calling thread is not associated with a Looper. * public static Looper myLooper() { * return sThreadLocal.get(); * } * 请看源码中的这两行注释: * 返回与当前线程相关联的Looper. * 如果当前线程没有一个与之相关联的Looper那么就返回null. * * 回看刚才的报错Can't create handler inside thread that has not called Looper.prepare(). * 提示我们调用Looper.prepare(). * 于是在子线程中这么写: * Looper.prepare(); * Handler handler=new Handler(); * 该报错消失. * * 既然调用Looper.prepare()可以消除该错误. * 那就看看Looper.prepare()的源码. * * /** Initialize the current thread as a looper. * * This gives you a chance to create handlers that then reference * * this looper, before actually starting the loop. Be sure to call * * {@link #loop()} after calling this method, and end it by calling * * {@link #quit()}. * public static void prepare() { * if (sThreadLocal.get() != null) { * throw new RuntimeException("Only one Looper may be created per thread"); * } * sThreadLocal.set(new Looper()); * } * 1 从注释文档可以看出该prepare()方法的作用: * 利用一个looper来初始化当前线程. * 2 在调用loop()方法前必须调用该prepare()方法. * 3 注意该方法的异常提示: * Only one Looper may be created per thread * 每一个线程只有一个Looper!!!! * * 在该方法的代码:sThreadLocal.set(new Looper()); * Looper构造方法如下: * private Looper() { * mQueue = new MessageQueue(); * mRun = true; * mThread = Thread.currentThread(); * } * 可以看到为sThreadLocal保存了一个Looper. * 所以在上述的myLooper()方法中调用sThreadLocal.get()就不再为空了. * * * 至此一个子线程中使用Handler的方式应该是这样的: * class LooperThread extends Thread { * public Handler mHandler; * * public void run() { * Looper.prepare(); * * mHandler = new Handler() { * public void handleMessage(Message msg) { * // process incoming messages here * } * }; * * Looper.loop(); * } * } * 该代码也是Google推荐的写法. * * * 在平常使的MainActivity中的UI线程中使用Handler时并没有调用Looper.prepare(); * 这是为什么呢? * 因为UI线程是主线程,系统已经自动帮我们调用了Looper.prepare()方法. * 在此不再赘述. * * * * 以上讨论了线程(Thread)和Looper的使用,下面讨论消息的发送和处理过程. * 平常最常用的方式: * handler.sendMessage(message); * 那么handleMessage(Message msg)中是怎么获取到刚发出的这条消息呢??? * Handler发送消息的具体方法有好几个但除了sendMessageAtFrontOfQueue(Message msg) * 以外的几个方法最终会调用sendMessageAtTime(Message msg, long uptimeMillis)方法: * * public boolean sendMessageAtTime(Message msg, long uptimeMillis){ * boolean sent = false; * MessageQueue queue = mQueue; * if (queue != null) { * msg.target = this; * sent = queue.enqueueMessage(msg, uptimeMillis); * } * else { * RuntimeException e = new RuntimeException( * this + " sendMessageAtTime() called with no mQueue"); * Log.w("Looper", e.getMessage(), e); * } * return sent; * } * * 在该方法中有两句代码很重要: * 1 msg.target = this; * 给msg设置了target. * 这里的this当然就是当前Handler对象本身! * 2 sent = queue.enqueueMessage(msg, uptimeMillis); * 将消息放入消息队列中. * 这里的queue(mQueue)就是上述Looper构造方法中的mQueue. * 在enqueueMessage(msg, uptimeMillis)方法中有一个队列. * 距离触发时间最短的message排在队列最前面,同理距离触发时间最长的message排在队列的最尾端. * 若调用sendMessageAtFrontOfQueue()方法发送消息它会调用该enqueueMessage(msg, uptimeMillis) * 来让消息入队只不过时间为延迟时间为0,即它会插入到队列头部. * * * 这就是消息的入队操作,那么消息怎么出队呢? * 这就要看Looper中的loop()方法 * public static final void loop() { * Looper me = myLooper(); * MessageQueue queue = me.mQueue; * while (true) { * Message msg = queue.next(); // might block * if (msg != null) { * if (msg.target == null) { * return; * } * if (me.mLogging!= null) me.mLogging.println( * ">>>>> Dispatching to " + msg.target + " "+ msg.callback + ": " + msg.what); * msg.target.dispatchMessage(msg); * if (me.mLogging!= null) me.mLogging.println( * "<<<<< Finished to " + msg.target + " "+ msg.callback); * msg.recycle(); * } * } * } * 在该方法中是一个死循环while(true),即Looper一直在轮询消息队列(MessageQueue) * 在该方法中有两句代码很重要: * 1 Message msg = queue.next(); * queue.next()消息队列的出列. * 2 msg.target.dispatchMessage(msg); * 用调用msg里的target的dispatchMessage()方法. * target是什么呢? * 参见上述sendMessageAtTime(Message msg, long uptimeMillis)可知: * target就是Handler!!!!在此回调了Handler的dispatchMessage方法,所以该消息就发送给了对应的Handler. * 接下来看Handler的dispatchMessage(Message msg)方法: * * public void dispatchMessage(Message msg) { * //1 message的callback * if (msg.callback != null) { * handleCallback(msg); * } else { * //2 handler的callback * if (mCallback != null) { * if (mCallback.handleMessage(msg)) { * return; * } * } * //3 Handler的handleMessage() * handleMessage(msg); * } * } * * 其中涉及到的CallBack为: * public interface Callback { * public boolean handleMessage(Message msg); * } * Handler的其中一个构造方法为: * Handler handler=new Handler(callback); * 所以在dispatchMessage(Message msg)涉及到了CallBack * 在绝大多数情况下message和Handler的callBack均为空. * 所以会调用dispatchMessage(Message msg)方法: * 这就回到了我们最熟悉的地方. * * * * ======================================================= * * Android异步消息机制中主要涉及到: * Thread Handler Looper MessageQueue * 它们的相互关系如下: * 1 Looper.prepare(); * 1.1 为当前线程生成对应的Looper. * 一个Looper包含了三个变量: * mQueue---->消息队列(MessageQueue) * mRun------>true * mThread--->当前线程 * 1.2 将该Looper保存到ThreadLocal中. * 之所以采用ThreadLocal来存放线程对应的Looper主要目的是确保 * 每个线程只有一个唯一的Looper. * * Looper和其所属线程的相互关联的代码体现: * 1.3 Looper.myLooper() * 得到与当前线程相关联的Looper * 1.4 Looper.myLooper().getThread() * 得到与Looper相关联的线程Thread * * 2 Handler handler=new Handler() * 在第一步中利用Looper.prepare()实现了Looper与线程的关联. * 在此接着看Handler与Looper的关系. * 注意看上述Handler的构造方法中的代码: * mLooper = Looper.myLooper(); * 这样就得到了与线程相关联的Looper. * mQueue = mLooper.mQueue; * 得到与线程相关联的Looper的消息队列(MessageQueue)mQueue * 在Handler的构造方法中就看出了Handler与Looper的关联. * * 小结: * (1) Looper.prepare(); * 实现了Looper和其所属线程的相互关联 * (2) Handler handler=new Handler() * 实现了Handler与Looper的关联. * (3) 一个线程可有多个Handler但只有一个Looper * * * ======================================================= * 了解Thread Handler Looper MessageQueue几者间的关系之后,在此总结 * Android异步消息机制的流程: * * 1 Looper.prepare(); * 实现Looper和其所属线程的相互关联 * 其中Looper包含了三个变量: * mQueue---->消息队列(MessageQueue) * mRun------>true * mThread--->当前线程 * 在执行该方法以后每个Looper中就存在一个消息队列(MessageQueue)了. * Handler发送的消息都会保存到该消息队列(MessageQueue)中. * 2 Handler handler=new Handler(); * 实现了Handler与Looper的关联. * 3 Looper.loop(); * 开始轮询消息队列(MessageQueue),并且会一直轮询. * 按照队列处理里面的每个消息.当然刚开始的时候该队列为空. * 4 mHandler.sendMessage(message)发送消息至消息队列. * 具体过程可以参见以上的详细描述. * 5 在第三步时Looper.loop()不是一直在轮询消息队列么? * 当消息(Message)出队时,找到该消息的target(其实就是一个Handler)回调 * 其dispatchMessage(Message msg)方法;在该方法中会调用到我们非常熟悉 * 的handleMessage(Message msg). * * 以上就是Android异步消息机制的详细流程. * 简单地可以这么说: * Handler------>发送消息和处理消息 * Looper------->用消息队列(MessageQueue)保存消息.使用loop()方法一直轮询消息队列. * 并在消息出队的时候将其发送给合适的Handler. * * 以上流程分析可参见流程图. * * ======================================================= * 参考资料: * 1 http://androidxref.com/4.0.4/ * 2 http://blog.csdn.net/guolin_blog/article/details/9991569 * 3 http://blog.csdn.net/aaa2832/article/details/7773220 * 4 http://blog.csdn.net/lilu_leo/article/details/8145320 * 5 http://blog.csdn.net/oney139/article/details/7922742 * 6 http://www.cnblogs.com/cqcmdwym/archive/2013/05/12/3074138.html * Thank you very much */ public class MainActivity extends Activity { private Thread mThread; private Handler mHandler; private final String TAG = "Handler"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); testHandler(); } private void testHandler() { mThread = new Thread(new Runnable() { @Override public void run() { Looper.prepare(); mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 123) { Log.i(TAG, "收到消息 msg.arg1=" + msg.arg1); } } }; Message message = new Message(); message.what = 123; message.arg1 = 456; mHandler.sendMessage(message); Looper.loop(); } }); mThread.start(); } }
附图如下:
main.xml如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:layout_centerInParent="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Handler原理学习" /> </RelativeLayout>
Handler详解系列(一)——Handler异步消息机制详解(附图)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。