handler机制原理

article/2025/11/6 2:27:55

一,什么是handler
handler是消息机制的一个上层接口  更新UI的操作 耗时完毕后发送消息给主线程更新UI
耗时操作只能在子线程中执行,Android是线程不安全的你不能在子线程中更新UI 所以Android引入了handler机制
handler通过发送和处理message和Runnable对象来关联相对应线程的MssagerQueue(消息队列)
1.可以让对应的message(message是放置信息,可以传递一些参数)和runnable(Runnable则是直接给出处理的方法)在未来的某个时间进行相应处理
2.让自己想要处理的耗时操作放在子线程,让更新UI操作放在主线程
3.队列就是依次执行,Handler会处理完一个消息或者执行完某个处理在进行下一步,这样不会出现多个线程同时要求进行UI处理而引发的混乱现象
二,handler的使用方法
1.post(runnable)  Runnable则是直接给出处理的方法)在未来的某个时间进行相应处理源码底层还是调用了sendmassage(massage)方法
2.sendmassage(massage)  放置信息,可以传递一些参数 ,可以定时处理更新UI
三,handler机制的原理

首先我们先看图


looper 
每一个线程所独有的,通过loop()方法读取MessageQueue当中的消息 读到消息后,把消息发送给Handler来进行处理
messageQueue
消息队列(先进先出的方式来管理的message),在创建looper的时候就创建了MESSagerQueue,已经关联到了一起
mssage
消息对象

handler
作用1.可以发送消息
作用2.处理消息(处理looper发送过来的消息)

1.1获取looper (mLooper = Looper.myLooper()),通过looper 获取massageQueue( mQueue = mLooper.mQueue;)

  /*** Use the {@link Looper} for the current thread with the specified callback interface* and set whether the handler should be asynchronous.** Handlers are synchronous by default unless this constructor is used to make* one that is strictly asynchronous.** Asynchronous messages represent interrupts or events that do not require global ordering* with respect to synchronous messages.  Asynchronous messages are not subject to* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.** @param callback The callback interface in which to handle messages, or null.* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.** @hide*/public Handler(Callback callback, boolean async) {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());}}mLooper = Looper.myLooper();if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}mQueue = mLooper.mQueue;mCallback = callback;mAsynchronous = async;}


1.2  looper是怎么获取的呢?
 

 // sThreadLocal.get() will return null unless you've called prepare().static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); /*** Return the Looper object associated with the current thread.  Returns* null if the calling thread is not associated with a Looper.*/public static @Nullable Looper myLooper() {return sThreadLocal.get();}

sThreadLocal.get()作用:通过不同的线程访问同一个ThreadLocal ,不管是get方法还是set方法对所做的读写操作仅限于各自线程内部,这就是为什么handler里边通过ThreadLocal 来保存looper 这样他就可以使每一个线程有单独唯一的looper

1.3 looper作用
 

 /*** 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.");}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();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);}final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;final long traceTag = me.mTraceTag;if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {Trace.traceBegin(traceTag, msg.target.getTraceName(msg));}final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();final long end;try {msg.target.dispatchMessage(msg);end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);}}if (slowDispatchThresholdMs > 0) {final long time = end - start;if (time > slowDispatchThresholdMs) {Slog.w(TAG, "Dispatch took " + time + "ms on "+ Thread.currentThread().getName() + ", h=" +msg.target + " cb=" + msg.callback + " msg=" + msg.what);}}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();}}

源码中loopr 就是创建了一个FOR的死循环,然后从消息队列中逐个的去获取消息,最后处理消息的过程 
总结:looper 是通过prepare 来创建LOOPER    把他保存在ThreadLocal中 然后通过lopper.loop()开启循环来进行消息的分发

   /** 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));}

源码中调用  msg.target.dispatchMessage(msg); target就是一个handler 是中转器   (/*package*/ Handler target;)

handler总结: 通过handler将消息传递给了消息队列(massageQueue) 而消息队列又将消息分发给了handler 来处理,这就是handler的2个作用,一个是接收,发送消息,一个是处理消息

四,handler引起的内存泄露及解决办法

handler发送的消息在当前handler的消息队列中,如果此时activity finish掉了,那么消息队列的消息依旧会由handler进行处理,若此时handler声明为内部类(非静态内部类),我们知道内部类天然持有外部类的实例引用,这样在GC垃圾回收机制进行回收时发现这个Activity居然还有其他引用存在,因而就不会去回收这个Activity,进而导致activity泄露。

解决方案:

1.把handler设置成静态内部类,因为静态内部类不持有外部类的引用,所以使用静态的handler不会导致activity的泄露

2.onDestroy生命周期中调用 handler.removeCallbacks();,进行释放

3.handler内部类持有外部activity的弱引用

 


 


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

相关文章

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

ORA-12162错误解决

新来的一个小伙儿&#xff0c;想学习Oracle我给了他文档&#xff0c;自己研究着&#xff0c;下午时段叫我过去&#xff0c;发现其在装oracle后&#xff0c;进行测试时报ORA-12162错误&#xff0c;正好本鸟之前也遇到过&#xff0c;这里面贴出了大家如有遇到不要惊慌。 报错图片…

ORA-12162: TNS:net service name is incorrectly specified

概述 因未设置系统环境变量ORACLE_SID导致ORA-12162错误 分析原因 首先登录数据库主机执行 oerr ora 12162 我们首先查看看下 tnsnames.ora文件 执行tnsping CC命令 检查下是否可以tnsping通&#xff0c;核对IP和端口以及实例名 数据库服务器端使用TNSNAMES.ORA中记录的…

Oracle“ ORA-12162:TNS:net服务名称指定不正确”错误和解决方案

During the connection to the Oracle Database server with the sqlplus we may get an error with the ORA-12162 specifier. This error will prevent to connect to the Oracle Database Server. 在使用sqlplus连接到Oracle数据库服务器的过程中,ORA-12162说明符可能会出错…