全球信息:我所知道的Handler
简单讲,handler就是两个功能
插入消息,enqueuemessage,msg,when从消息队列中遍历所有消息,比对msg.when和当前的when,找到合适的位置插入
处理消息,looper.loop会从messagequeue中调用next。取消息,如果消息还没到时间该执行,就会比对时间,下次轮询就通过binder写入,native函数休眠,到时间唤醒执行。
(资料图片仅供参考)
handler内存泄漏
GCRoot 一般是静态变量或者常量可以作为GCROOTGCROOT 是ThreadLocal,存在于Looper中,Looper被加载就存在,handler持有activity或者fragment,handler又被message持有,message的target属性,message被messagequeue持有,messagequeue被looper中的threadlocal持有
java中匿名内部类会默认持有外部类的引用
打断持有链
- handler.removemesage handler.removecallbacks
- handler使用static修饰。
主线程的Looper不允许退出
处理消息,looper取出来后,调用message.tager.dispatchemesage后面调用了handler的handlemessage方法。
还有个callback对象,如果有callback,dispatch会先执行callback的处理,calllback返回true,后面就不处理了,callback返回false就给handler的handlemessage处理了
Meesage对象创建
message创建用的obtain,池化,频繁的创建销毁会导致内存不稳定,抖动,造成卡顿 oom等问题
message pool的最大缓存50
阻塞和休眠,阻塞是被动的,休眠是主动的,阻塞不会让出cpu,休眠会,thread.yield会让出cpu。
子线程主线程通信
handler,livedata,eventbus,flow,rxjava,broadcast,观察者模式不能跨线程
最终都是handler完成的。
Handler监听卡顿
原理是在looper内完成的,looper处理消息的时候,会打印内容,就是Printer,looper可以设置它。
Message消息的分类
同步消息,普通的消息都是同步消息异步消息,创建handler的时候设置async为true即可同步屏障,需要通过反射调用,app层无法直接调用,是messagequeue提供的posSyncBarrier方法实现的,返回一个token,时msg的arg1值,用它来取消同步屏障。和普通消息的区别是,msg。target属性为null。
刷新UI的消息是异步消息,发送前先插入了一个同步屏障消息,异步消息处理完成后,要将同步屏障消息移除队列
消息入队
handler消息加入队列,有一系列方法,如下:
// 发送空消息public final boolean sendEmptyMessage(int what){}public final boolean sendEmptyMessageDelayed(int what,long delay){}public final boolean sendEmptyMessageAtTime(int what,long when){}// 发送消息public final boolean sendMessage(@NonNull Message msg){}public final boolean sendMessageDelayed(@NonNull Message msg,long time){}public final boolean sendMessageAtTime(@NonNull Message msg,long when){}public final boolean sendMessageAtFrontOfQueue(Message msg) {}// post发送public final boolean post(@NonNull Runnable r) {}public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {}public final boolean postAtTime( @NonNull Runnable r, @Nullable Object token, long uptimeMillis) {}public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {}public final boolean postDelayed(Runnable r, int what, long delayMillis) {}public final boolean postDelayed( @NonNull Runnable r, @Nullable Object token, long delayMillis) {}public final boolean postAtFrontOfQueue(@NonNull Runnable r) {}// enqueuepublic final boolean executeOrSendMessage(@NonNull Message msg) {}private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) {}
最终都是掉用的enqueueMessage加入队列
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { msg.target = this; // 绑定消息处理对象 msg.workSourceUid = ThreadLocalWorkSource.getUid(); if (mAsynchronous) { // 根据handler创建是否是异步的,来将消息标记为异步消息 msg.setAsynchronous(true); } // 调用messagequeue的方法,加入队列。 return queue.enqueueMessage(msg, uptimeMillis); }
下面看下消息如何入队的
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } synchronized (this) { if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; // 当前队列的头 Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. // 消息队列为null,或者消息是立刻执行的,或者要执行的时间先于头消息的时间,将当前消息作为新的队列的头 msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don"t have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); // 遍历队列,找到合适的位置,将当前消息插入进去 Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // 如果需要的话,唤醒队列,开始处理消息,就是MessageQueue的next方法开始执行 // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
取消息以及消息处理
取消息1
取消息入口是Looper的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; // 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); me.mSlowDeliveryDetected = false; // 无限循环,通过loopOnce取消息 for (;;) { if (!loopOnce(me, ident, thresholdOverride)) { return; } } }
处理消息
private static boolean loopOnce(final Looper me, final long ident, final int thresholdOverride) { // 取消息 Message msg = me.mQueue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return false; } // This must be in a local variable, in case a UI event sets the logger final 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属性,就是handler对象,进行消息的处理。注意:屏障消息是没有target的。 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 (me.mSlowDeliveryDetected) { if ((dispatchStart - msg.when) <= 10) { Slog.w(TAG, "Drained"); me.mSlowDeliveryDetected = false; } } else { if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery", msg)) { // Once we write a slow delivery log, suppress until the queue drains. me.mSlowDeliveryDetected = 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(); return true; }
取消息2
再看下实际的取消息的方法,MessageQueue的next方法
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } // 这个方法会在没有消息的时候阻塞 nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; // 消息头保存 Message msg = mMessages; if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. // 这里处理同步屏障消息,如果当前消息是异步消息,就跳出循环,否则继续循环 do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); // 循环的作用,找出队列里的异步消息,存储在msg里,或者将同步屏障消息存储在msg中,prevMsg存储的是同步消息 } if (msg != null) { if (now < msg.when) { // 下次的唤醒时间 // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); // 将取出的消息交给handler处理。 return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; } }
handler的消息处理
是从dispatchMessage方法开始的,里面进行消息处理
public void dispatchMessage(@NonNull Message msg) {// 检查message是否设置了callback对象(runnable类型的)if (msg.callback != null) {// 执行其run方法handleCallback(msg);} else {// 检查handler是否设置了callback对象if (mCallback != null) {if (mCallback.handleMessage(msg)) {// 如果返回true,后续就不执行了。return;}}// 自定义handler的时候,实现该方法处理消息handleMessage(msg);}}
IdleHandler是什么?干什么?继续看MessageQueue类。
IdleHandler是MessageQueue的静态内部接口。如下
public static interface IdleHandler { /** * Called when the message queue has run out of messages and will now * wait for more. Return true to keep your idle handler active, false * to have it removed. This may be called if there are still messages * pending in the queue, but they are all scheduled to be dispatched * after the current time. */ boolean queueIdle(); }
当前线程的消息对立内当前没有消息要处理时,会取出idlehander执行任务,因为执行在主线程,禁止执行耗时操作。返回true表示执行完并不会移除该对象,false执行完一次就移除。而且,执行时机是不确定的。执行的地方在next方法内部。
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } // 执行到这里,说明没有找到message对象需要执行了,且线程没有退出。 // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } // 取出idlehandlers if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { // 执行idlehandler的方法 keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { // 需要移除的话,在此处进行移除 synchronized (this) { mIdleHandlers.remove(idler); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; } }
标签:
为您推荐
广告
- 全球信息:我所知道的Handler
- 卜算子咏梅零落成泥碾作尘_卜算子咏梅中的 ldquo 无意苦争春 一任群芳妒 零落成泥碾作尘 只有香 天天简讯
- 网络卡顿怎么解决(网络卡)
- 天天热点!漩涡鸣人怎么死的
- 每日快播:美媒列出湖人第二场击败掘金充满信心的三个原因,八村塁有望首发
- dnf哪个区人最多最活跃2023_dnf哪个区人最多-当前热点
- 环球头条:荣信文化(301231.SZ):公司产品暂不涉及ChatGPT的应用
- 交通部启动应急响应机制,全力搜救倾覆远洋渔船“鲁蓬远渔028” 全球热闻
- 龙建路桥股份有限公司拟在哈尔滨双城区成立全资子公司-每日快播
- 全球速看:在绿茵上挥杆角逐!“韵味杭州”西湖国际高尔夫公开赛即将开赛!
- “大脚超市”里的电商经济:听主播“月月”讲述象牙山里的创业故事
- 2023国考财政部拟录用公务员和参公单位人员公示公告|世界视点
- 环球视讯!微分和导数的区别李永乐 微分和导数的区别
- 【世界速看料】深业集团18亿元公司债将在深交所上市 利率3.1%
- 湖南衡东:坚持“快清严实”工作标准,护航“三湘护农”走深走实
- 上榜丨4月,吉林挖掘机平均作业量同比增长全国第一!
- 环球报道:施华洛世奇官网潮品推荐:如何搭出风采、戴出魅力?
- 记者内马尔愿大幅度降薪重返巴萨 西媒内马尔希望回巴萨但拉波尔塔未考虑签回他-环球快资讯
- 5月17日 13:20分 太极集团(600129)股价快速拉升|全球快讯
- 剧透来了!世界智能大会这个论坛即将在武清举办! 全球焦点
广告
- 【新要闻】海贼王索隆的刀阎魔_海贼王索隆的刀
- 麦当劳中国称今年全年预计招募超过15万名员工_世界微头条
- 天天热讯:两天内多家美国巨头破产! 穆迪:破产潮才刚刚开始……
- 每日聚焦:打击倒票“黄牛”,推进实名制是关键一招
- 汉王科技预计2024年初推出首款内测版大模型
- 倒计时1天 | 全球首款海上光伏异质结组件即将重磅发布!
- “小小少年进军营”少先队课外实践活动举行“零距离”感受军营魅力
- 强对流天气蓝色预警 9省区市部分地区有8至10级雷暴大风或冰雹|每日焦点
- 社区凝聚爱,拉杆箱公益集市用爱连线“沪滇”-资讯推荐
- 盘龙区举办“5·12护士节”系列庆祝活动
- 世界观天下!从供热展“碳”寻空气能源新布局
- 环球动态:广州浪奇股价一字涨停,转型食品及文化产业园总市值超66亿元
- 本周15只新股申购 五只新股上市集中在周一 环球快看
- CBA季后赛400分,4人做到,刘玉栋封神
- 存款利率全方位下行是否会触发存款搬家,谁会成为替代品? 天天热资讯
- 环球快看点丨揉面不光滑是什么原因(揉面不光滑是什么原因?谢谢!)
- 滨岩:阿根廷中国行世界杯全员主力来,杯也来!-观天下
- 百联股份:5月15日融券卖出金额25.88万元,占当日流出金额的0.23%
- 黑龙江省双鸭山市友谊县红兴隆世纪小区_双鸭山市友谊县贴吧_天天微头条
- 朱鹮怎么读音是什么_朱鹮怎么读