热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ThreadLocal、Looper、Handler、MessageQueue

一、ThreadLocal线程局部存储(ThreadLocalStorage)当从同一线程中引用该变量时,其值总是相同;而从

一、ThreadLocal

    线程局部存储(ThreadLocalStorage)当从同一线程中引用该变量时,其值总是相同;而从不同的线程中引用该变量时,其值应该不同。

        作用域:

方法成员变量:仅在方法内部有效

类成员变量:仅在对象内部有效

线程局部存储(TLS)变量:在本线程内的任何对象内保持一致

静态变量:在本进程内的任何对象内保持一致

跨进程通信(IPC)变量:一般使用 Binder 定义,在所有进程中保持

 

二、Looper

./frameworks/base/core/java/android/os/Looper.java

作用:

    1、调用该类中的静态方法 prepare() 创建一个消息队列

     在 Looper 的静态方法 prepare() 中,给线程局部存储变量中添加一个新的 Looper 对象:

public static final void prepare() {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper());}

    Looper 的构造方法中创建了一个 MessageQueue 对象:

private Looper() {mQueue = new MessageQueue();mRun = true;mThread = Thread.currentThread();}

   2、提供静态方法 loop() ,使调用该方法的线程进行无限循环,并从消息队列中读取消息

    对程序员来讲,当需要把一个线程变为异步消息处理线程时,应该在 Thread 类的 run() 方法中先调用 Looper.prepare() 为该线程创建一个 MessageQueue 对象,然后再调用 Looper.loop() 方法,使当前线程进入消息处理循环

    

1 public static final void loop() {
2 Looper me = myLooper();
3 MessageQueue queue = me.mQueue;
4 while (true) {
5 Message msg = queue.next(); // might block
6 //if (!me.mRun) {
7 // break;
8 //}
9
10 if (msg != null) {
11 if (msg.target == null) {
12 // No target is a magic identifier for the quit message.
13 return;
14 }
15 if (me.mLogging!= null) me.mLogging.println(
16 ">>>>> Dispatching to " + msg.target + " "
17 + msg.callback + ": " + msg.what
18 );
19
20 msg.target.dispatchMessage(msg);
21
22 if (me.mLogging!= null) me.mLogging.println(
23 "<<<<24 &#43; msg.callback);
25
26 msg.recycle();
27 }
28 }
29 }

1&#xff09;调用 myLooper() 函数返回当前线程的 Looper对象&#xff0c;调用 sThreadLocal.get() 方法返回当前线程 ID 对应的 Looper 对象

public static final Looper myLooper() {return (Looper)sThreadLocal.get();}

2&#xff09;通过 Looper 对象获取线程的消息队列

MessageQueue queue &#61; me.mQueue;

3&#xff09;进入 while 无限循环

    &#xff08;1&#xff09;调用MessageQueue 对象的 next() 方法取出队列中的消息&#xff08;如果当前队列为空&#xff0c;则当前线程会被挂起&#xff0c;也就是说 next() 内部会暂停当前线程&#xff09;

Message msg &#61; queue.next();

    &#xff08;2&#xff09;回调 dispatchMessage(msg) 完成对消息的处理。msg 变量的类型是 Message&#xff0c; msg.target 的类型是 Handler

msg.target.dispatchMessage(msg);

    &#xff08;3&#xff09;处理完消息后&#xff0c;调用 msg.recycle() 回收 Message 对象占用的系统资源。因为 Message 类内部使用了一个数据池保存 Message 对象&#xff0c;从而避免不停的创建和删除 Message 对象&#xff0c;因此每次处理完该消息后需要将 Message 对象表明为空闲状态

 

三、MessageQueue

./frameworks/base/core/java/android/os/MessageQueue.java

1、消息在 MessageQueue 中以 Message 表示&#xff0c;消息以链表的结构进行保存

2、MessageQueue 中两个主要的方法“取出消息”和“添加消息”

    1&#xff09;取出消息 next()

final Message next() {...// Try to retrieve the next message, returning if found.synchronized (this) {now &#61; SystemClock.uptimeMillis();Message msg &#61; pullNextLocked(now);if (msg !&#61; null) return msg;if (tryIdle && mIdleHandlers.size() > 0) {idlers &#61; mIdleHandlers.toArray();}}...}

    2&#xff09;添加消息 enquenceMessage()

    

final boolean enqueueMessage(Message msg, long when) {...synchronized (this) {...msg.when &#61; when;//Log.d("MessageQueue", "Enqueing: " &#43; msg);Message p &#61; mMessages;if (p &#61;&#61; null || when &#61;&#61; 0 || when < p.when) {msg.next &#61; p;mMessages &#61; msg;this.notify();} else {Message prev &#61; null;while (p !&#61; null && p.when <&#61; when) {prev &#61; p;p &#61; p.next;}msg.next &#61; prev.next;prev.next &#61; msg;this.notify();}}return true;}

 

四、Handler

./frameworks/base/core/java/android/os/Handler.java

       1、尽管 MessageQueue 提供了直接读/写的函数接口&#xff0c;但对于应用程序员而言一般不直接读/写消息队列。在Looper.loop() 函数中&#xff0c;当取出消息后回调 msg.target 的 handleMessage() 方法&#xff0c;而 msg.target 的类型正是 Handler。

public void dispatchMessage(Message msg) {if (msg.callback !&#61; null) {handleCallback(msg);} else {if (mCallback !&#61; null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}

       我们一般用 Handler 类向消息队列中发送消息&#xff0c;并重载 Handler 类的 handleMessage() 函数添加消息处理代码。

       2、Handler 对象只能添加到有消息队列的线程中&#xff0c;否则发生异常

mLooper &#61; Looper.myLooper();
if (mLooper &#61;&#61; null) {throw new RuntimeException("Can&#39;t create handler inside thread that has not called Looper.prepare()");
}

       因此&#xff0c;在构造 Handler 对象前必须已经执行过 Looper.prepare() &#xff0c;但 Looper.prepare() 不能被执行两次。

 

五、UI线程和自定义 Thread的区别

1、UI线程是从 ActivityThread 运行的&#xff0c;在 ActivityThread 中的 main() 方法中已经使用 Looper.prepareMainLooper() 为线程添加了 Looper 对象&#xff08;即已经创建了消息队列MessageQueue&#xff09;

2、自定义 Thread 是一个裸线程&#xff0c;因此不能直接在 Thread 中定义 Handler对象

转:https://www.cnblogs.com/zhouyuchao/archive/2012/08/20/2647901.html



推荐阅读
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • 解决.net项目中未注册“microsoft.ACE.oledb.12.0”提供程序的方法
    在开发.net项目中,通过microsoft.ACE.oledb读取excel文件信息时,报错“未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序”。本文提供了解决这个问题的方法,包括错误描述和代码示例。通过注册提供程序和修改连接字符串,可以成功读取excel文件信息。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 本文介绍了MVP架构模式及其在国庆技术博客中的应用。MVP架构模式是一种演变自MVC架构的新模式,其中View和Model之间的通信通过Presenter进行。相比MVC架构,MVP架构将交互逻辑放在Presenter内部,而View直接从Model中读取数据而不是通过Controller。本文还探讨了MVP架构在国庆技术博客中的具体应用。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
author-avatar
黑鸽子
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有