不知庐山真面目只缘身在此山中,讲解一下Handler

o(╥﹏╥)o今天太困了,啥也不想干。就把之前收集的资料拿出来温习一下刚好和大家共享下

之前在" rel="nofollow">GitHub
上更新了些关于面试类的文章,含有从基础到进阶。含有BATJ.字节跳动面试专题,算法专题,高端技术专题,混合开发专题,java面试专题,Android,Java小知识,到性能优化.线程.View.OpenCV.NDK等应有尽有。还有辅之相关的视频+学习笔记

(更多完整项目下载。未完待续。源码。图文知识后续上传github。)
可以点击关于我联系我获取完整PDF
(VX:mm14525201314)

Handler

1、谈谈消息机制 Handler 作用 ?

负责跨线程通信,这是因为在主线程不能做耗时操作,而子线程不能更新 UI,所以当子线程中进行耗时操作后需要更新 UI时,通过 Handler 将有关 UI 的操作切换到主线程中执行。

具体分为四大要素:

  • Message(消息): 需要被传递的消息,消息分为硬件
    产生的消息(如按钮、触摸)和软件生成的消息。
  • MessageQueue(消息队列): 负责消息的存储与管
    理,负责管理由 Handler 发送过来的 Message。读取
    会自动删除消息,单链表维护,插入和删除上有优势。
    在其 next()方法中会无限循环,不断判断是否有消息,
    有就返回这条消息并移除。
  • Handler(消息处理器): 负责 Message 的发送及处
    理。主要向消息池发送各种消息事件
    Handler.sendMessage())和处理相应消息事件
    Handler.handleMessage()),按照先进先出执行,
    内部使用的是单链表的结构。
  • Looper(消息池): 负责关联线程以及消息的分发,在
    该线程下从 MessageQueue 获取 Message,分发给Handler,Looper 创建的时候会创建一个MessageQueue,调用 loop()方法的时候消息循环开始,其中会不断调用 messageQueue 的 next()方法,当有消息就处理,否则阻塞在 messageQueue 的next()方法中。当 Looperquit()被调用的时候会调用
    messageQueuequit(),此时 next()会返回 null,然
    loop()方法也就跟着退出。

具体流程如下:

  • 在主线程创建的时候会创建一个 Looper,同时也会在在
    Looper 内部创建一个消息队列。而在创键 Handler 的
    时候取出当前线程的 Looper,并通过该 Looper 对象获
    得消息队列,然后 Handler 在子线程中通过MessageQueue.enqueueMessage 在消息队列中添
    加一条 Message
  • 通过 Looper.loop() 开启消息循环不断轮询调用
    MessageQueue.next(),取得对应的 Message 并且
    通过 Handler.dispatchMessage 传递给 Handler,最
    终调用 Handler.handlerMessage 处理消息。

2、一个线程能否创建多个 Handler,Handler 跟 Looper 之间的对应关系 ?

  • 一个 Thread 只能有一个 Looper,一个 MessageQueen,可以有多个 Handler
  • 以一个线程为基准,他们的数量级关系是: Thread(1) :
    Looper(1) : MessageQueue(1) : Handler(N)

3、软引用跟弱引用的区别

  • 软引用(SoftReference): 如果一个对象只具有软引用,则
    内存空间充足时,垃圾回收器就不会回收它;如果内存空间不
    足了,就会回收这些对象的内存。只要垃圾回收器没有回收
    它,该对象就可以一直被程序使用。
  • 弱引用(WeakReference): 如果一个对象只具有弱引用,那
    么在垃圾回收器线程扫描的过程中,一旦发现了只具有弱引用
    的对象,不管当前内存空间足够与否,都会回收它的内存。
  • 两者之间根本区别在于: 只具有弱引用的对象拥有更短暂的生
    命周期,可能随时被回收。而只具有软引用的对象只有当内存
    不够的时候才被回收,在内存足够的时候,通常不被回收

4、Handler 引起的内存泄露原因以及最佳解决方案

为什么会导致泄露:
Handler 允许我们发送延时消息,如果在延时期间用户
关闭了 Activity,那么该 Activity 会泄露。 这个泄露
是因为 Message 会持有 Handler,而又因为 Java 的
特性,内部类会持有外部类,使得 Activity 会被
Handler 持有,这样最终就导致 Activity 泄露。

怎么解决:
将 Handler 定义成静态的内部类,在内部持有
Activity 的弱引用,并在 AcitivityonDestroy()
调用 handler.removeCallbacksAndMessages(null)
及时移除所有消息。

5、为什么系统不建议在子线程访问 UI?

Android 的 UI 控件不是线程安全的,如果在多线程中并发访问
可能会导致 UI 控件处于不可预期的状态

这时你可能会问为何系统不对 UI 控件的访问加上锁机制呢?因为:

  • 加锁机制会让 UI 访问逻辑变的复杂
  • 加锁机制会降低 UI 的访问效率,因为加锁会阻塞某些线
    程的执行

    6、Looper 死循环为什么不会导致应用卡死?

  • 主线程的主要方法就是消息循环,一旦退出消息循环,那么你的应用也就退出了,Looer.loop()方法可能会引起主线程的阻塞,但只要它的消息循环没有被阻塞,能一直处理事件就不
    会产生 ANR 异常。
  • 造成 ANR 的不是主线程阻塞,而是主线程的 Looper消息处理过程发生了任务阻塞,无法响应手势操作,不能及时刷新 UI。
  • 阻塞与程序无响应没有必然关系,虽然主线程在没有消息可处理的时候是阻塞的,但是只要保证有消息的时候能够立刻处
    理,程序是不会无响应的。

7、使用 Handler 的 postDealy 后消息队列会有什么变化?

如果队列中只有这个消息,那么消息不会被发送,而是计算到
时唤醒的时间,先将 Looper 阻塞,到时间就唤醒它。但如果此时要加入新消息,该消息队列的对头跟 delay 时间相比更长,
则插入到头部,按照触发时间进行排序,队头的时间最小、队
尾的时间最大

8丶子线程直接 new 一个 Handler 吗?怎么做?

不可以,因为在主线程中,Activity 内部包含一个 Looper 对
象,它会自动管理 Looper,处理子线程中发送过来的消息。而
对于子线程而言,没有任何对象帮助我们维护 Looper 对象,所以需要我们自己手动维护。所以要在子线程开启 Handler 要先
创建 Looper,并开启 Looper 循环

9、Message 可以如何创建?哪种效果更好,为什么?

可以通过三种方法创建:

  • 直接生成实例 Message m = new Message
  • 通过 Message m = Message.obtain
  • 通过 Message m = mHandler.obtainMessage()

后两者效果更好,因为 Android 默认的消息池中消息数量是 10,而后
两者是直接在消息池中取出一个 Message 实例,这样做就可以避免多
生成 Message 实例。

请查看完整的PDF版
(更多完整项目下载。未完待续。源码。图文知识后续上传github。)
可以点击关于我联系我获取完整PDF
(VX:mm14525201314)

原文地址:https://blog.51cto.com/14541311/2452403

时间: 2024-10-10 18:41:38

不知庐山真面目只缘身在此山中,讲解一下Handler的相关文章

Drupal建站过程思考——不识庐山真面目,只缘身在此山中

使用drupal与没有使用drupal,在网站的设计过程上本质是一样的!所以,我们在使用drupal建一个新网站时,要时刻联想自己在没有drupal时是如何设计开发的. 不使用drupal时,我们通常按如下步骤设计开发一个网站: 1.静态页面设计.主要包括主页.二级列表页.二级内容页三类页面,制作过程一般是:PS效果图->切图->HTML/CSS/JS静态页.通常一个优秀的美工/前端工程师,可以搞定这一切,比如我们的XiaoLu童鞋. 2.静态页面整理/页面布局整理——对代码进行切块.拆分.在

你可能不知道的字符比较中的“秘密”

原文:你可能不知道的字符比较中的"秘密" 有时候,一个简单的字符比较,你可能也会被弄得晕头转向.为什么这样说呢?请看下面这个例子(代码就不贴了,因为后来发现页面不支持这两个字符的显示).猜测一下,会是什么结果?是1还是0? 回答这个问题之前,请再继续向下看.先创建几个不同排序规则的数据库(见数据库名可知). Figure-1: 在SQL_Latin1_General_CP1_CI_AS排序规则下的比较 Figure-2: 在Chinese_PRC_CI_AS排序规则下的比较 在SQL_

转载:android笔记--android中的多线程--Handler, Looper, MessageQueue, Message类

什么时候使用多线程: 1. 耗时操作使用多线程, 耗时操作放在UI线程中会导致用户的操作无法得到响应. 2. 阻塞操作使用多线程, 理由同上. 3. 多核CUP的设备使用多线程, 可以有效提高CPU的利用率. 4. 并行操作使用多线程. android中的多线程模型主要涉及的类有:Looper, Handler, MessageQueue, Message等. 一:Looper类: 1 static final ThreadLocal<Looper> sThreadLocal = new Th

Android中Thread、Handler、Looper、MessageQueue的原理分析

在Android开发当中,Thread.Handler.Looper这几个类是特别常见,在刚开始学习Android的时候对这些类可能并不是很清晰.下面我们就一起从源码的角度剖析一下这几个类的工作原理. Thread 首先是Thread, 我们都知道一个Thread就是一个线程对象,只要在run方法中填写自己的代码然后启动该线程就可以实现多线程操作.例如 : new Thread(){ public void run() { // 耗时的操作 }; }.start(); 我们知道,针对上面的代码中

山中访友

好文章集锦目录 原文链接:https://baike.baidu.com/item/%E5%B1%B1%E4%B8%AD%E8%AE%BF%E5%8F%8B/9053149?fr=aladdin 走出门,就与微风撞了满怀,风中含着露水和栀子花气息的微风撞了个满怀.早晨,好清爽!心里的感觉好清爽! 不坐车,不邀游伴,也不带什么礼物,就带着满怀的好心情,踏一条幽径,独自去访问我的朋友. 那座古桥,是我要拜访的第一个老朋友.啊·,老桥,你如一位德高望重的老人,在这涧水上站了几百年了吧?你把多少人马渡过

在单线程模型中 Message、Handler、Message Queue、Looper 之间的关系

Message,信息的载体,用来传递数据给Handler. Handler (Handler处理者,是 Message 的主要处理者,负责 Message 的发送,Message 内容的执行处理)发送和处理Message和Runable对象,这些对象和一个线程的MessageQueue相关联.每一个线程实例和一个单独的线程以及该线程的 MessageQueue 相关联.Handler和创建它的线程绑定在一起,把 Message和Runable 对象传递给 MessageQueue,这些对象离开

Android面试题 请解释下单线程模型中Message、Handler、MessageQueue、Looper之间的关系

简单的说,Handler获取当前线程中的looper对象,looper用来存放从MessageQueue中取出的Message,再由Handler进行Message分发和处理,按照先进先出执行. MessageQueue(消息队列):用来存放通过Handler发送的消息,通常附属于某一个创建它的线程,可以通过Looper.myQueue()得到当前线程的消息队列. Handler:是Message的主要处理者,负责Message的发送,Message内容的执行处理.例如将消息发送到消息队列(se

单线程模型中Message、Handler、MessageQueue、Looper之间的关系

Handler简介: 一个Handler允许你发送和处理Message和Runable对象,这些对象和一个线程的MessageQueue相关联.每一个线程实例和一个单独的线程以及该线程的MessageQueue相关联.当你创建一个新的Handler时,它就和创建它的线程绑定在一起了.这里,线程我们也可以理解为线程的MessageQueue.从这一点上来看,Handler把Message和Runable对象传递给MessageQueue,而且在这些对象离开MessageQueue时,Handler

TimerTask中如何通过Handler交由UIThread更新UI

有两种方式: 第一种:post给Handler // 开启线程刷新 handler = new Handler(); timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { handler.post(new Runnable() { @Override public void run() { mTextLat.setText(AppContext.getLatitude()+"&quo