站在线程角度看Android Handler 机制
来源:互联网 发布:几个淘宝号刷一天挣40 编辑:程序博客网 时间:2024/05/16 06:55
Android开发的小伙伴们对Handler机制应该非常熟悉了,Handler机制主要用于完成两个线程间互相通信,最典型的应用就是在子线程中执行一系列任务,然后通知UI线程更新UI。主要与Handler 、 Looper 、Message 这三个类相关,其中Handler负责处理(发送,接收并处理)消息,Looper负责维护一个消息队列,并通过一个不断的循环从队列里取出消息交给Handler处理,Message就是消息本身。看过不少文章,作为菜鸟的我心中还是有一些疑问,终于抽时间好好梳理了一下,不多说,直接上问题。
为了方便描述,我们把UI线程成为主线程,把其他我们自己创建的线程,称为子线程。
问题一:Handler机制中如何将两个线程联系起来,站在线程的角度,各个线程都做了些啥,执行了哪些语句?
问题二:UI线程需要处理很多事务且不能堵塞,一个线程只有一个控制权,一个线程中sendMessage之后,处理线程的控制权会在什么时机处理发送过来的message?
问题三:与UI线程绑定的Handler的postDelay(Runnable r, Long delay)方法什么情况下会阻塞UI线程?为啥?
从问题一开始,先看一个典型的场景:子线程执行了handler.sendMessage(1),结果是主线程执行了handleMessage方法里的textView.setText();
public class MainActivity extends Activity { private TextView textView; private Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { textView.setText("Handler"); }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView =(TextView) findViewById(R.id.textView); new Thread(){ public void run() {//这里是子线程在执行 try { Thread.sleep(1000); handler.sendEmptyMessage(1); // } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }; }.start(); } }
很多文章介绍过,handler的send相关方法和post相关方法最终都会到sendMessageAtTime(Message msg, long uptimeMillis)。我们这里重点关注线程的执行步骤,或者说线程控制权的一步一步转移,看我们handler.sendMessage(1)之后的调用链:
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }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); }private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this;//记住这个target,this就是Handler的一个对象了。。。 if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
子线程执行了handler.sendMessage(1),所以以上调用都是子线程来执行的。在最后一个方法enqueueMessage()中,msg的target指向当前的Handler对象handler。
所以,例子中的子线程的控制权在以下方法中转移:sendMessage(Message msg)
--->sendMessageDelayed(Message msg, long delayMillis)
--->sendMessageAtTime(Message msg, long uptimeMillis)
--->enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
--->enqueueMessage() // 这个enqueueMessage是MessageQueue的方法。
所以子线程就是往handler相关联的MessageQueue里丢了一条Message,然后控制权会回到该线程的run()方法里,去执行handler.sendMessage(1)的下一条语句,在我们的例子里,run()方法执行完毕。子线程进入死亡状态,线程对象等待被回收。
在Handler机制中,Looper负责从MessageQueue中取出message并做相应处理。子线程既然往handler对应的MessageQueue里丢了一条Message,那肯定这MessageQueue对应的线程和对应的Looper在处理这条message。在sendMessageAtTime(Message msg, long uptimeMillis)方法中,看到所操作的消息队列mQueue,看一下这个mQueue是什么时候被赋与对象(值)或者说初始化的。
找到一个Handler的构造:
public Handler(Callback callback, boolean async) { 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; }例子中在为handler实例化的时候使用的无参构造直接调用了上面的构造:
public Handler() { this(null, false); }
因为我们是在主线程中创建的hadler, 构造方法中调用Looper.myLooper()来获取Looper对象也就自然归属于主线程,也就是说这里的Looper和MessageQueue都是与UI线程相对应的,那例子中丢到这个messageQueue里的Message也自然由UI线程去取,并在loop()方法中执行msg.target.dispatchMessage(msg),这里msg.target就是刚刚被赋予的handler,最终由UI线程执行dispatchMessage()来处理这条消息。Looper.myLooper()来获取Looper对象期间的线程对应关系后边再分析。
public static void loop() {//创建Handler的线程必然执行了这个loop(),主线程默认执行了Looper.prepare()和Looper.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; for (;;) {//这个是主线程对应的Looper的loop方法的话,主线程的控制权会在这个for循环里直到App退出。 Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } msg.target.dispatchMessage(msg);//target 就是发送该msg的Handler对象 msg.recycleUnchecked(); } }来看dispatchMessage:
public void dispatchMessage(Message msg) { if (msg.callback != null) {//handler执行post相关的方法时,丢进来的Runnble对象会作为msg.callback,优先执行该回调 handleCallback(msg);// } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) {//看到回调的的handleMessage,我们并没有在定义Handler的时候生成该回调。 return; } } handleMessage(msg);//终于执行了我们在Handler定义时覆写的handleMessage了!!!! } }所以,UI线程执行了Looper 的 prepare()和loop()并在loop的无限循环中执行了Handler的dispatchMessage(Message msg)和handleMessage(Message msg)。
总结:Handler对应一个线程,一个线程对应且只对应一个Looper对象,一个线程对应且只对应一个MessageQueue。(注意,没有说一个线程对应一个handler)
在发送消息阶段,Handler sendMessage可以在任意线程发出,但是发出去的message会被入队到该发出消息的Handler对应的线程关联的消息队列。
在消息的处理阶段,也是由发出消息的Handler对应的线程通过Looper.loop()方法来处理消息。
到这里就可以从逻辑上解释,为什么我们自己新建的线程必须执行Looper.prepare()和Looper.loop()方法才能创建handler,因为不执行Looper.prepare(),handler发送的message不知道入队到哪个消息队列,发送消息无从谈起,不执行Looper.loop(),线程就没有不断地从消息队列取消息并处理的过程,处理消息也就无从谈起。
当然,其实Handler提供了可以传入Looper参数的构造,子线程可以在不执行Looper.prepare()和Looper.loop()方法的情况下通过传入一个Looper对象创建handler,这时Looper对应的线程即是该handler对应的线程。所谓Looper对应的线程由其prepare()方法的调用线程决定。看Looper的prepare()方法:
public static void prepare() { prepare(true); } 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)); }
prepare()方法通过ThreadLocal给执行prepare的线程绑定了一个Looper对象。
ThreadLocal提供一种线程的变量管理机制,主要是set(Tvalue)和get(T value)方法,通过调用sThreadLocal.set(T value)可以为调用线程设置一个变量,这里就是给执行prepare的线程设置了一个looper对象,也是通过这个机制,保证了线程与Looper的一对一关系。上文提到的Looper.myLooper()获取的looper对象就是通过这个机制获取的。
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
通过sThreadLocal.get()就可以获取当前线程调用set方法时设置的值(对象),ThreadLocal保证了不同的线程调用get时,得到的变量都是该线程set的值(对象)。
到这里就可以解答问题二了,在一个线程发送message后,对应的处理线程必须执行Looper.loop(),它的控制权就一定在loop()方法中不断循环,才能及时从消息队列中取出消息做相应的处理。
问题三,先说答案:UI线程绑定的Handler的postDelay(Runnable r, Long delay)方法delay多长时间都不会阻塞UI线程,但是r的run()方法如果执行时间过长,会阻塞。
参考文章:
《线程的私家小院:ThreadLocal》点击打开链接
鸿洋大神的:点击打开链接
《Android中的Handler解析》点击打开链接
- 站在线程角度看Android Handler 机制
- 【Android】从源码角度看Handler机制
- Android Handler机制详解:在线程中新建Handler
- 重新从源码的角度看Handler消息通信机制
- Android Handler 线程消息机制
- Android 从源码看Handler消息机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Android的消息机制,用Android线程间通信的Message机制,Android中Handler的使用方法——在子线程中更新界面,handler机制
- Sort Sort Sort
- Cows POJ
- 实验一:顺序表
- Java + TestNG + Appium 实现单机多个Android终端并发测试
- MPU6050的数据获取、分析与处理
- 站在线程角度看Android Handler 机制
- 数据结构 2 算法
- Codeforces Round #436 div 2 A B C D 题解
- Mysql Mac osX 下安装及初始设置和问题解决
- 为什么设置了jackson2序列化方式hash的get方法不能反序列化对象
- vs2012设置查看内存
- 脚本化HTTP——AJax
- 内容提供器总结
- Android HAL概述