Android - 浅谈 Handler 机制

article/2025/11/6 2:23:36

        熟悉 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 机制可以加深记忆和更深的使用。因此,这次就不写实例了~~~

感谢 菜鸟教程 。

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


http://chatgpt.dhexx.cn/article/if2dvjDR.shtml

相关文章

【Android】Handler机制详解

【Android】Handler机制详解 本文是在 Carson带你学Android 作者的异步通信 专栏中Handler的基础上学习整理的kotlin版本&#xff0c;并且Android源码部分也更新至最新。 1.使用Handler消息传递机制的原因 2.相关概念 概念定义作用备注Main Thread应用程序初次启动时会自动开…

一文搞懂Handler机制原理

前言 Android提供了Handler来满足线程间的通信&#xff0c;开发中不管直接还是间接基本离不开Handler的使用&#xff0c;通常Handler被我们用来做子线程更新UI线程的工具&#xff0c;可以说只要有子线程与主线程通信的地方就会有Handler。 工欲善其事必先利其器&#xff0c;熟…

handler机制原理

一,什么是handler handler是消息机制的一个上层接口 更新UI的操作 耗时完毕后发送消息给主线程更新UI 耗时操作只能在子线程中执行,Android是线程不安全的你不能在子线程中更新UI 所以Android引入了handler机制 handler通过发送和处理message和Runnable对象来关联相对应线程的…

Android Handler消息机制原理最全解读(持续补充中)

本文主要详细去解读Android开发中最常使用的Handler&#xff0c;以及使用过程中遇到的各种各样的疑问。 Handler 在Android开发的过程中&#xff0c;我们常常会将耗时的一些操作放在子线程&#xff08;work thread&#xff09;中去执行&#xff0c;然后将执行的结果告诉UI线程…

Handler机制

1.为何引入Handler机制 Handler是线程间通讯的机制&#xff0c;Android中&#xff0c;网络访问、文件处理等耗时操作必须放到子线程中去执行&#xff0c;否则将会造成ANR异常。 ANR异常&#xff1a;Application Not Response 应用程序无响应 产生ANR异常的原因&#xff1a;在…

Handle消息机制解析

概述 Handler消息机制(由Handler/Looper/MessageQueue等构成)&#xff0c;Android有大量的消息驱动方法来进行交互&#xff0c;就像Android的四大组件(Activity、Service、Broadcast、ContentProvider)的启动过程交互&#xff0c;都离不开Handler的消息机制&#xff0c;所以An…

Handler机制(一)——Handler运行流程分析

1 概述 Handler机制是Android的异步消息处理机制&#xff0c;用于在线程间传递消息&#xff0c;主要涉及到四部分&#xff1a;Handler、Looper、Message和MessageQueue。其中Handler是消息的发送者和处理者&#xff1b;Message是消息主体&#xff1b;MessageQueue是消息队列&a…

reshape的作用

reshape就是矩阵的变换就是行和列相乘的数相等就可以相互变换

reshape函数

在opencv中&#xff0c;reshape函数比较有意思&#xff0c;它既可以改变矩阵的通道数&#xff0c;又可以对矩阵元素进行序列化&#xff0c;非常有用的一个函数。 函数原型&#xff1a; C: Mat Mat::reshape(int cn, int rows0) const 参数比较少&#xff0c;但设置的时候却要千…

Reshape的命令应用

import numpy as np tnp.arange(0,64).reshape(8,8) print(t) Reshape 的参考使用&#xff1a; (1条消息) Python的reshape的用法&#xff1a;reshape(1,-1)_冷月无声的博客-CSDN博客_reshape函数pythonhttps://blog.csdn.net/qq_29831163/article/details/90112000Reshape主…

matlab中reshape的用法,reshape2 函数 reshape 的用法

函数 reshape 的用法 请我在MATLAB编程中遇到了一个问题&#xff0c;函数reshape的用法我就是没有弄B reshape(A,m,n) 返回一个m*n的矩阵B&#xff0c; B中元素是按列从A中得到的。如果A中元素个数没有m*n个&#xff0c; 则会引发错误。 B reshape(A,m,n,p,...)和B reshape(…

Numpy之reshape()详解

Numpy中reshape的使用方法为:numpy.reshape(a, newshape, orderC) 参数详解&#xff1a;1.a: type:array_like(伪数组&#xff0c;可以看成是对数组的扩展&#xff0c;但是不影响原始数组。) 需要reshape的array2.newshape:新的数组 新形状应与原形状兼容。如果是整数&#xf…

利用Numpy库的方法reshape()对ndarray对象矩阵的形状进行调整

利用Numpy库的函数reshape()对ndarray对象矩阵的形状进行调整 调整矩阵或图像的形状是一个常用的操作。 在Numpy库中&#xff0c;可使用函数reshape()实现此操作。 其函数原型如下&#xff1a; dst numpy.reshape(a, newshape[, orderC])参数意义如下&#xff1a; a—需要调…

关于reshape

X.reshape(X.shape[0], -1).T和X.reshape(-1&#xff0c;X.shape[0]) 虽然矩阵形式仍然一致但矩阵元素排列完全不同 在降低测试集维度时注意&#xff0c;应使用X.reshape(X.shape[0], -1).T

matlab reshape 用法,函数 reshape 的用法

函数 reshape 的用法别问小编过得好不好不好你也帮助不了好也不是你的功劳。 请小编在MATLAB编程中遇到了一个问题,函数reshape的用法小编就是没有弄B = reshape(A,m,n) 返回一个m*n的矩阵B, B中元素是按列从A中得到的。如果A中元素个数没有m*n个, 则会引发错误。 你知道失望…

【晕头晕脑的Python】Python中Reshape函数解析

Reshape函数解析 Reshape()作用&#xff1a;Reshape()实例说明&#xff1a;一维reshape() 为 二维二维数组 reshape 切片&#xff0c;逆置三维Reshape情况 Reshape()作用&#xff1a; Reshape&#xff08;&#xff09;&#xff0c;函数的作用就是将数据的按照既定的维度进行整…

python中reshape的用法

python中reshape的用法 reshape函数的使用&#xff1a; #reshape&#xff08;&#xff09;是数组对象中的方法&#xff0c;用于改变数组的形状 arr [1,2,3,4,5,6,7,8,9] import numpy as np arrnp.array(arr) #一维 #变成一个3 * 3的二维矩阵&#xff1a; #方法一 arr.resha…

Python的reshape的用法

numpy中reshape函数的三种常见相关用法 reshape(1,-1)转化成1行&#xff1a; reshape(2,-1)转换成两行&#xff1a; reshape(-1,1)转换成1列&#xff1a; reshape(-1,2)转化成两列 numpy中reshape函数的三种常见相关用法 numpy.arange(n).reshape(a, b) 依次生成n个自然…

python中reshape函数用法详解

python中reshape函数用法详解 reshape函数 reshape函数是Numpy库中的一个函数&#xff0c;可以用于改变一个数组的形状&#xff0c;例如将一个二维数组转换成一个三维数组。 import numpy as np # 创建一个二维数组&#xff0c;形状为(4, 6) a np.array([[1, 2, 3, 4, 5, 6]…

ORA-12162 :TNS 指定的网络服务名不正确

原因&#xff1a;这台服务器有多个库 在环境变量文件/home/oracle/.bash_profile中也没有export ORACLE_SIDxxx 解决方法&#xff1a; 登录前先export ORACLE_SIDxxx 再确保查看一下echo $ORACLE_SID 再登录sqlplus / as sysdba