Looper

Looper是线程用来运行消息循环(message loop)的类。默认情况下,线程并没有与之关联的Looper,可以通过在线程中调用Looper.prepare() 方法来获取,并通过Looper.loop() 无限循环地获取并分发MessageQueue中的消息,直到所有消息全部处理。典型用法如下:

 1 public class LooperThread extends Thread {
 2     @Override
 3     public void run() {
 4         // 将当前线程初始化为Looper线程
 5         Looper.prepare();
 6
 7         // ...其他处理,如实例化handler
 8
 9         // 开始循环处理消息队列
10         Looper.loop();
11     }
12 }

通过上面两行核心代码,你的线程就升级为Looper线程了

1)Looper.prepare()

通过上图可以看到,现在你的线程中有一个Looper对象,它的内部维护了一个消息队列MessageQueue。一个Thread只能有一个Looper对象

 1 public class Looper {
 2     // 每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象
 3     private static final ThreadLocal sThreadLocal = new ThreadLocal();
 4     // Looper内的消息队列
 5     final MessageQueue mQueue;
 6     // 当前线程
 7     Thread mThread;
 8     // 。。。其他属性
 9
10     // 每个Looper对象中有它的消息队列,和它所属的线程
11     private Looper() {
12         mQueue = new MessageQueue();
13         mRun = true;
14         mThread = Thread.currentThread();
15     }
16
17     // 我们调用该方法会在调用线程的TLS中创建Looper对象
18     public static final void prepare() {
19         if (sThreadLocal.get() != null) {
20             // 试图在有Looper的线程中再次创建Looper将抛出异常
21             throw new RuntimeException("Only one Looper may be created per thread");
22         }
23         sThreadLocal.set(new Looper());
24     }
25     // 其他方法

通过源码,prepare()背后的工作方式一目了然,其核心就是将looper对象定义为ThreadLocal。如果你还不清楚什么是ThreadLocal,请参考《理解ThreadLocal》

2)Looper.loop()

调用loop方法后,Looper线程就开始真正工作了,它不断从自己的MQ中取出队头的消息(也叫任务)执行。其源码分析如下:

 1 public static final void loop() {
 2         Looper me = myLooper();  //得到当前线程Looper
 3         MessageQueue queue = me.mQueue;  //得到当前looper的MQ
 4
 5         // 这两行没看懂= = 不过不影响理解
 6         Binder.clearCallingIdentity();
 7         final long ident = Binder.clearCallingIdentity();
 8         // 开始循环
 9         while (true) {
10             Message msg = queue.next(); // 取出message
11             if (msg != null) {
12                 if (msg.target == null) {
13                     // message没有target为结束信号,退出循环
14                     return;
15                 }
16                 // 日志。。。
17                 if (me.mLogging!= null) me.mLogging.println(
18                         ">>>>> Dispatching to " + msg.target + " "
19                         + msg.callback + ": " + msg.what
20                         );
21                 // 非常重要!将真正的处理工作交给message的target,即后面要讲的handler
22                 msg.target.dispatchMessage(msg);
23                 // 还是日志。。。
24                 if (me.mLogging!= null) me.mLogging.println(
25                         "<<<<< Finished to    " + msg.target + " "
26                         + msg.callback);
27
28                 // 下面没看懂,同样不影响理解
29                 final long newIdent = Binder.clearCallingIdentity();
30                 if (ident != newIdent) {
31                     Log.wtf("Looper", "Thread identity changed from 0x"
32                             + Long.toHexString(ident) + " to 0x"
33                             + Long.toHexString(newIdent) + " while dispatching to "
34                             + msg.target.getClass().getName() + " "
35                             + msg.callback + " what=" + msg.what);
36                 }
37                 // 回收message资源
38                 msg.recycle();
39             }
40         }
41     }

除了prepare()和loop()方法,Looper类还提供了一些有用的方法,比如

Looper.myLooper()得到当前线程looper对象:

1 public static final Looper myLooper() {
2         // 在任意线程调用Looper.myLooper()返回的都是那个线程的looper
3         return (Looper)sThreadLocal.get();
4     }

getThread()得到looper对象所属线程:

1  public Thread getThread() {
2         return mThread;
3     }

Handler 避免内存泄漏

  1. handler.removeCallbacksAndMessages(null);
  2. handler.getLooper().quit();
Looper的quit()方法结束looper循环:
public final class Looper {// sThreadLocal.get() will return null unless you've called prepare().static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();private static Looper sMainLooper;  // guarded by Looper.classfinal MessageQueue mQueue;   public void quit() {mQueue.quit(false);//调用MessageQueue退出} }

MessageQueue 的quit()方法
public final class MessageQueue {//...void quit(boolean safe) {if (!mQuitAllowed) {throw new IllegalStateException("Main thread not allowed to quit.");}synchronized (this) {if (mQuitting) {return;}mQuitting = true;if (safe) {removeAllFutureMessagesLocked();} else {removeAllMessagesLocked();}// We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);}}private void removeAllMessagesLocked() {Message p = mMessages;while (p != null) {Message n = p.next;p.recycleUnchecked();p = n;}mMessages = null;}private void removeAllFutureMessagesLocked() {final long now = SystemClock.uptimeMillis();Message p = mMessages;if (p != null) {if (p.when > now) {removeAllMessagesLocked();} else {Message n;for (;;) {n = p.next;if (n == null) {return;}if (n.when > now) {break;}p = n;}p.next = null;do {p = n;n = p.next;p.recycleUnchecked();} while (n != null);}}}//...
}

通过观察以上源码我们可以发现:
当我们调用Looper的quit方法时,实际上执行了MessageQueue中的removeAllMessagesLocked方法,该方法的作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。
当我们调用Looper的quitSafely方法时,实际上执行了MessageQueue中的removeAllFutureMessagesLocked方法,通过名字就可以看出,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。
无论是调用了quit方法还是quitSafely方法只会,Looper就不再接收新的消息。即在调用了Looper的quit或quitSafely方法之后,消息循环就终结了,这时候再通过Handler调用sendMessage或post等方法发送消息时均返回false,表示消息没有成功放入消息队列MessageQueue中,因为消息队列已经退出了

到此为止,你应该对Looper有了基本的了解,总结几点:

1.每个线程有且最多只能有一个Looper对象,它是一个ThreadLocal

2.Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行

3.Looper使一个线程变成Looper线程。

handler

什么是handler?handler扮演了往MQ上添加消息和处理消息的角色(只处理由自己发出的消息),即通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。handler创建时会关联一个looper,默认的构造方法将关联当前线程的looper,不过这也是可以set的。默认的构造方法:

public class handler {final MessageQueue mQueue;  // 关联的MQfinal Looper mLooper;  // 关联的looperfinal Callback mCallback; // 其他属性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());}}// 默认将关联当前线程的loopermLooper = Looper.myLooper();// looper不能为空,即该默认的构造方法只能在looper线程中使用if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}// 重要!!!直接把关联looper的MQ作为自己的MQ,因此它的消息将发送到关联looper的MQ上mQueue = mLooper.mQueue;mCallback = null;}// 其他方法
}

 这里有一个疑问,如果handler在主线程的死循环一直运行是不是特别消耗CPU资源呢?

这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

下面我们就可以为之前的LooperThread类加入Handler:

 1 public class LooperThread extends Thread {
 2     private Handler handler1;
 3     private Handler handler2;
 4
 5     @Override
 6     public void run() {
 7         // 将当前线程初始化为Looper线程
 8         Looper.prepare();
 9
10         // 实例化两个handler
11         handler1 = new Handler();
12         handler2 = new Handler();
13
14         // 开始循环处理消息队列
15         Looper.loop();
16     }
17 }

可以看到,一个线程可以有多个Handler,但是只能有一个Looper!

转载于:https://www.cnblogs.com/mingfeng002/p/3142277.html

Looper,Handler, MessageQueue相关推荐

  1. Handler、Looper与MessageQueue源码分析

    在Android中可以通过Handler来更新主线程中UI的变化,更新UI只能在主线程中进行更新,而为了让其他线程也能控制UI的变化,Android提供了一种机制Handler.Looper与Mess ...

  2. Android面试题目之六---Handler,Looper和MessageQueue深入研究

    [size=x-large]1.Handler, MessageQueue, Looper 之间的关系图.[/size] a. Looper相当于一个引擎,从队列中获取消息,然后发送给Handler. ...

  3. Android 中Message,MessageQueue,Looper,Handler详解+实例

    一.几个关键概念 1.MessageQueue:是一种数据结构,见名知义,就是一个消息队列,存放消息的地方.每一个线程最多只可以拥有一个MessageQueue数据结构. 创建一个线程的时候,并不会自 ...

  4. android 结束if循环_Android 消息机制(Handler + MessageQueue + Looper)

    Author:CrazyWah Date:2018.03.26 CopyRight:http://crazywah.com 禁止搬运!!!禁止搬运!!!禁止搬运!!! Android的消息机制主要由H ...

  5. Message,MessageQueue,Looper,Handler详解+实例

    Message,MessageQueue,Looper,Handler详解+实例 原文地址 Android的Handler使用(这篇简单介绍Handler的使用) 一.几个关键概念 1.Message ...

  6. MessageQueue Message Looper Handler的解释说明

    总结4个关键概念  1.MessageQueue:是一种数据结构,见名知义,就是一个消息队列,存放消息的地方.每一个线程最多只可以拥有一个MessageQueue数据结构.  创建一个线程的时候,并不 ...

  7. Android 系统(177)---Android消息机制分析:Handler、Looper、MessageQueue源码分析

    Android消息机制分析:Handler.Looper.MessageQueue源码分析 1.前言 关于Handler消息机制的博客实际上是非常多的了. 之前也是看别人的博客过来的,但是过了一段时间 ...

  8. Android 系统(18)---Handler,MessageQueue与Looper关系

    一说到Android的消息机制,自然就会联想到Handler,我们知道Handler是Android消息机制的上层接口,因此我们在开发过程中也只需要和Handler交互即可,很多人认为Handler的 ...

  9. Handler与looper、MessageQueue的关系

    Handler与Looper.MessageQueue的关系 1. Handler机制? 只要遵循Android使用handler来更新UI的机制,我们就不用关心多线程的问题,所有的更新UI的操作都是 ...

最新文章

  1. framer x使用教程_如何使用Framer Motion将交互式动画和页面过渡添加到Next.js Web应用程序
  2. 《西河大鼓——夸轿车》(唱词文本)
  3. 2016matlab安装
  4. Java:注解和反射
  5. python 删除断点_给python 初学者的四条忠告,减少一些没必要的麻烦
  6. python 调用文件传参_Python读取ini配置文件传参的简单示例
  7. xis表格怎么打印_Excel做的表格怎么打印
  8. 图片怎么去底色?怎么去图片背景为透明?
  9. 【文学】平凡的世界第三部
  10. Java获取电脑外网ip地址方法
  11. A股-入门-新手该如何成功选股
  12. 如何实现微信抢红包算法?
  13. 基于MMS街景的导航数据采集方法研究
  14. 复盘 20160629
  15. 考研 研究生 什么是考研 考研的第一课 全面了解考研 研究生
  16. 网站seo优化推广方式有哪些?如何提高网站排名?
  17. 【ZHYP002】子涵优品开发日志
  18. “提升业务决策效率:探索FICO Blaze决策引擎的应用“
  19. php广告联盟,PHPCPS广告联盟系统
  20. Java中多态的使用

热门文章

  1. 微信小程序-测试游戏生成六边多边形
  2. qt +ChartDirector 绘制图表
  3. 使用CAJViewer 提取PDF文件中的文字
  4. [原创]如何从数据库层面检测两表内容的一致性
  5. LeetCode数据库 177. 第N高的薪水
  6. C++ STL学习笔记(2) 容器结构与分类
  7. 问题 J: A+B Problem (II) : Input/Output Practice 山东科技大学OJ C语言
  8. python向it新增5个元素_Python序列、元组、列表、集合及字典笔记整理
  9. simpledateformat格式_大厂都是怎么用Java8代替SimpleDateFormat?
  10. mysql哪一款好用_用了这么多年MySql,这些好习惯你用过哪些