熟悉 Android 开发的小伙伴都知道,不能再非主线程中修改 UI 控件,而且当时老师告诉我们就是在非主线程的代码修改 UI 控件可以用 Handler。以至于后来我也都是这么做的,最近有人问我这个自定义的 Handler 为啥下面都是黄色的,是不是他写的不对。于是点进去看了下源码,发现原来我用了这么多年的构造早就是被废弃了的,而我还在坚持。

This Handler class should be static or leaks might occur (anonymous android.os.Handler)
@SuppressLint("HandlerLeak")

查看源码发现只是构造函数被废弃了

给出的解释如下

Default constructor associates this handler with the Looper for the current thread. If this thread does not have a looper, this handler won't be able to receive messages so an exception is thrown.
Deprecated
Implicitly choosing a Looper during Handler construction can lead to bugs where operations are silently lost (if the Handler is not expecting new tasks and quits), crashes (if a handler is sometimes created on a thread without a Looper active), or race conditions, where the thread a handler is associated with is not what the author anticipated. Instead, use an java.util.concurrent.Executor or specify the Looper explicitly, using Looper.getMainLooper, {link android.view.View#getHandler}, or similar. If the implicit thread local behavior is required for compatibility, use new Handler(Looper.myLooper()) to make it clear to readers.

译文:
默认构造函数将此处理程序与当前线程的循环器关联。如果这个线程没有循环程序,这个处理程序将无法接收消息,因此会抛出一个异常。
弃用
隐式地选择一个电影在处理程序建设会导致错误操作在哪里默默地丢失(如果处理程序不期望新任务和退出),崩溃(如果一个处理程序有时没有电影活动)上创建一个线程,或者竞态条件,相关的线程处理程序并不是作者的预期。相反,使用java.util.concurrent. execute

刚好借此机会,简单了解一下 Handler 的机制。(以下内容部分来源于菜鸟教程)

  • UI线程:就是我们的主线程,系统在创建UI线程的时候会初始化一个Looper对象,同时也会创建一个与其关联的MessageQueue;
  • Handler:作用就是发送与处理信息,如果希望Handler正常工作,在当前线程中要有一个Looper对象
  • Message:Handler接收与处理的消息对象
  • MessageQueue:消息队列,先进先出管理Message,在初始化Looper对象时会创建一个与之关联的MessageQueue;
  • Looper:每个线程只能够有一个Looper,管理MessageQueue,不断地从中取出Message分发给对应的Handler处理!

流程图如下

也就是说当我们需要在子线程的执行后修改 UI 控件时,可以新建一个 Handler 对象,通过 Handler 的方法发送消息,在新建的 Handler 对象中执行修改 UI 的结果。

Handler 的相关方法

  • void handleMessage(Message msg):处理消息的方法,通常是用于被重写!
  • sendEmptyMessage(int what):发送空消息
  • sendEmptyMessageDelayed(int what,long delayMillis):指定延时多少毫秒后发送空信息
  • sendMessage(Message msg):立即发送信息
  • sendMessageDelayed(Message msg):指定延时多少毫秒后发送信息
  • final boolean hasMessage(int what):检查消息队列中是否包含what属性为指定值的消息 如果是参数为(int what,Object object):除了判断what属性,还需要判断Object属性是否为指定对象的消息

我们先来简单的看一下源码部分。

Looper

Looper 比较重要的两个方法是 prepare( ) 和 loop( )

构造方法如下:

    private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();}

在创建时会新建一个 MessageQueue,然后通过 prepare 方法创建 Looper,结果通过 ThreadLocal 保存,这个我在源码里没找到这个类的注释,但根据下面可以看到。

// sThreadLocal.get() will return null unless you've called prepare().

译文:除非你调用了prepare(),否则sThreadLocal.get()将返回null。

    // sThreadLocal.get() will return null unless you've called prepare().@UnsupportedAppUsagestatic final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();/** 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() {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));}/*** Initialize the current thread as a looper, marking it as an* application's main looper. See also: {@link #prepare()}** @deprecated The main looper for your application is created by the Android environment,*   so you should never need to call this function yourself.*/@Deprecatedpublic static void prepareMainLooper() {prepare(false);synchronized (Looper.class) {if (sMainLooper != null) {throw new IllegalStateException("The main Looper has already been prepared.");}sMainLooper = myLooper();}}

prepareMainLooper 方法的注释则是更加直白,我们不需要创建,主程序会自动创建的。

接下来的 loop 方法就比较重要了,当我们在子线程中使用 Handler 则必须手动调用 Looper.prepare() 和 Looper.loop()。我们可以看到这段代码获取到 MessageQueue 后开启消息循环,不断从 MessageQueue 中取消息,无则阻塞,等待消息。有则调用 msg.target.dispatchMessage(msg) 处理消息。因此,Looper 只负责开启消息循环接收消息并处理消息。处理完消息后会调用 msg.recycleUnchecked() 来回收消息。

/*** Run the message queue in this thread. Be sure to call* {@link #quit()} to end the loop.*/public static void loop() {final Looper me = myLooper();if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}if (me.mInLoop) {Slog.w(TAG, "Loop again would have the queued messages be executed"+ " before this one completed.");}me.mInLoop = true;final MessageQueue queue = me.mQueue;// Make sure the identity of this thread is that of the local process,// and keep track of what that identity token actually is.Binder.clearCallingIdentity();final long ident = Binder.clearCallingIdentity();// Allow overriding a threshold with a system prop. e.g.// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'final int thresholdOverride =SystemProperties.getInt("log.looper."+ Process.myUid() + "."+ Thread.currentThread().getName()+ ".slow", 0);boolean slowDeliveryDetected = false;for (;;) {Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;}// This must be in a local variable, in case a UI event sets the loggerfinal Printer logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}// Make sure the observer won't change while processing a transaction.final Observer observer = sObserver;final long traceTag = me.mTraceTag;long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;if (thresholdOverride > 0) {slowDispatchThresholdMs = thresholdOverride;slowDeliveryThresholdMs = thresholdOverride;}final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);final boolean needStartTime = logSlowDelivery || logSlowDispatch;final boolean needEndTime = logSlowDispatch;if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {Trace.traceBegin(traceTag, msg.target.getTraceName(msg));}final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;final long dispatchEnd;Object token = null;if (observer != null) {token = observer.messageDispatchStarting();}long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);try {msg.target.dispatchMessage(msg);if (observer != null) {observer.messageDispatched(token, msg);}dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;} catch (Exception exception) {if (observer != null) {observer.dispatchingThrewException(token, msg, exception);}throw exception;} finally {ThreadLocalWorkSource.restore(origWorkSource);if (traceTag != 0) {Trace.traceEnd(traceTag);}}if (logSlowDelivery) {if (slowDeliveryDetected) {if ((dispatchStart - msg.when) <= 10) {Slog.w(TAG, "Drained");slowDeliveryDetected = false;}} else {if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",msg)) {// Once we write a slow delivery log, suppress until the queue drains.slowDeliveryDetected = true;}}}if (logSlowDispatch) {showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);}if (logging != null) {logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);}// Make sure that during the course of dispatching the// identity of the thread wasn't corrupted.final long newIdent = Binder.clearCallingIdentity();if (ident != newIdent) {Log.wtf(TAG, "Thread identity changed from 0x"+ Long.toHexString(ident) + " to 0x"+ Long.toHexString(newIdent) + " while dispatching to "+ msg.target.getClass().getName() + " "+ msg.callback + " what=" + msg.what);}msg.recycleUnchecked();}}

Looper 提供的退出的方法则为

 /*** Quits the looper.* <p>* Causes the {@link #loop} method to terminate without processing any* more messages in the message queue.* </p><p>* Any attempt to post messages to the queue after the looper is asked to quit will fail.* For example, the {@link Handler#sendMessage(Message)} method will return false.* </p><p class="note">* Using this method may be unsafe because some messages may not be delivered* before the looper terminates.  Consider using {@link #quitSafely} instead to ensure* that all pending work is completed in an orderly manner.* </p>** @see #quitSafely*/public void quit() {mQueue.quit(false);}/*** Quits the looper safely.* <p>* Causes the {@link #loop} method to terminate as soon as all remaining messages* in the message queue that are already due to be delivered have been handled.* However pending delayed messages with due times in the future will not be* delivered before the loop terminates.* </p><p>* Any attempt to post messages to the queue after the looper is asked to quit will fail.* For example, the {@link Handler#sendMessage(Message)} method will return false.* </p>*/public void quitSafely() {mQueue.quit(true);}

Message

  1. 使用 what 来区分消息
  2. 使用 arg1、arg2、obj、data 来传递数据
  3. 参数 target,它决定了 Message 所关联的 Handler

MessageQueue

  1. enqueueMessage 方法往消息列表中插入一条数据,
  2. next 方法从消息队列中取出一条消息并将其从消息队列中移除
  3. quit 方法退出消息列表,通过参数 safe 决定是否直接退出

Handler

构造函数大家可以查看源码,这里就不一一列举了,只提一下我原来一直用的被废弃的方法,而且我相信有很多人还在这样用~

Java 构造函数,也叫构造方法,是 Java中一种特殊的函数。与函数名相同,无返回值。

作用:一般用来初始化成员属性和成员方法的,即 new 对象产生后,就调用了对象的属性和方法。

因此感觉这部分不太重要,只要写的东西能实现所需功能就行了吧(出问题再改(手动狗头))。我看到网上好多人都这么写了。

    private final Handler handler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(@NonNull Message msg) {return false;}});

最后,简单了解了下 Handler 机制可以加深记忆和更深的使用。因此,这次就不写实例了~~~

感谢 菜鸟教程 。

如有侵权问题,请联系我。

Android - 浅谈 Handler 机制相关推荐

  1. 浅谈Handler机制

    Android中Handler是一个十分重要的东西,很多时候都需要用到Handler.那什么是Handler呢?又为什么要用Handler呢? 什么是Handler? 我们知道Android更新UI的 ...

  2. 浅谈Attention机制

    浅谈Attention机制 Attention注意机制现在大热,很多深度学习的框架都带上了注意力机制,而且也取得了很好的性能指标.乘着大热也来水一水文章,发表发表自己的看法.事先说明老哥我在NLP上萌 ...

  3. Android 浅谈 Activity (下)

    Android 浅谈 Activity(中) 上节讲了数据传递和数据回传,这节讲一讲状态保存以及启动模式. 在使用Bundle传递数据时,要注意,Bundle的大小是有限制的 < 0.5MB,如 ...

  4. 浅谈Attention机制的作用

    浅谈注意力机制的作用 前言 什么是注意力机制 空间注意力 对于时间步的注意力机制 1.数据集的制作 2.建立注意力模型 3.建立整体神经网络 4.完整代码 5.实验效果 通道注意力机制 前言 Atte ...

  5. 浅谈Android中的Handler机制

    Handler是Android中提供的一种异步回调机制,也可以理解为线程间的消息机制.为了避免ANR,我们通常会把一些耗时操作(比如:网络请求.I/O操作.复杂计算等)放到子线程中去执行,而当子线程需 ...

  6. Android中的Handler机制

    直接在UI线程中开启子线程来更新TextView显示的内容,运行程序我们会发现,如下错 误:android.view.ViewRoot$CalledFromWrongThreadException: ...

  7. android handler的机制和原理_第一百八十回:Android中的Handler机制九

    各位看官们大家好,上一回中咱们说的是Android中Handler机制的例子,这一回咱们继续说该例子.闲话休提,言归正转.让我们一起Talk Android吧! 看官们,由于时间的原因我们在上一回中只 ...

  8. android中handler机制,如何使用?,Android中的Handler机制

    一.Handler概述 二.Handler发送消息的方法 三.MessageQueue的enqueueMessage() 四.Message的when字段 五.子线程中使用Handler 六.Loop ...

  9. 【Android 异步操作】Handler 机制 ( Handler 常用用法 | HandlerThread 简介 | HandlerThread 源码注释分析 )

    文章目录 一.Handler 常用用法 二.HandlerThread 简介 三.HandlerThread 源码 一.Handler 常用用法 主线程 Handler 主要作用 : Looper 和 ...

  10. 【Android 异步操作】Handler 机制 ( Android 提供的 Handler 源码解析 | Handler 构造与消息分发 | MessageQueue 消息队列相关方法 )

    文章目录 一.Handler 构造函数 二.Handler 消息分发 三.MessageQueue 消息队列相关函数 一.Handler 构造函数 一般使用 Handler 时 , 调用 Handle ...

最新文章

  1. 传统运维团队转型应该注意哪些问题?
  2. 二级c语言上机编程技巧,二级C语言上机编程题技巧总结
  3. 现代谱估计:MTM 谐波分析
  4. 有望取代Spark,Michael Jordan和Ion Stoica提出下一代分布式实时机器学习框架Ray牛在哪?...
  5. 蓝桥杯 - 翻硬币(贪心)
  6. 最大化_怎样保证油压缓冲器工作效率最大化?
  7. Silverlight教程第二部分:使用布局管理 (木野狐译)
  8. java ojdbc 还需要装 oracle client 吗,ojdbc连接数据库
  9. (34)Verilog HDL算术运算:加减乘除运算
  10. 吴恩达神经网络和深度学习-学习笔记-25-定位数据不匹配
  11. 20200812每日一句
  12. linux/windows查询文件特定内容并写入目标文件
  13. 2020、2021年FRM一级二级notes
  14. 软件测试是干什么的 什么样的人才能够当软件测试员?
  15. Math.cbrt() Math.sqrt() Math.pow()
  16. 虚拟贝司拓展音源-Toontrack Acoustic EBX
  17. android屏幕坏 操作手机,手机屏幕碎了怎么备份操作?
  18. DRC设计规则设置介绍-Design Compiler(四)
  19. 【报告分享】2021年中国互联网保险消费者洞察报告-凯度元保清华大学国家金融研究院(附下载)
  20. idea java svn 设置_idea配置svn,随时拉取和上传代码的正确做法

热门文章

  1. JAVA ECXCEL 考勤导入查询
  2. 在 Vue 项目中引入 tinymce 富文本编辑器
  3. 政策评估计量经济学模型(DID)
  4. ssm图书馆管理系统
  5. php开发oa系统的插件下载不了,什么是oa系统软件
  6. 阻击 瑞星 和 雅虎助手 的 SVOHOST.exe(第2版)
  7. sqlserver200864位下载_microsoft sql server 2008官方下载|Microsoft SQL Server 200832/64位 完整版_ - 极光下载站...
  8. 最早的支付网关(滴滴支付)和最新的聚合支付设计架构
  9. 【历史上的今天】5 月 31 日:Amiga 之父诞生;BASIC 语言的共同开发者出生;黑莓 BBM 停运
  10. 网络爬虫——淘宝网页面分析思路