首页 > 其他分享 >我所知道的Handler

我所知道的Handler

时间:2023-05-17 23:45:48浏览次数:33  
标签:boolean next Handler msg message null final 知道

简单讲,handler就是两个功能

插入消息,enqueuemessage,msg,when
从消息队列中遍历所有消息,比对msg.when和当前的when,找到合适的位置插入

处理消息,looper.loop会从messagequeue中调用next。取消息,如果消息还没到时间该执行,就会比对时间,下次轮询就通过binder写入,native函数休眠,到时间唤醒执行。

handler内存泄漏

GCRoot 一般是静态变量或者常量可以作为GCROOT
GCROOT 是ThreadLocal,存在于Looper中,Looper被加载就存在,
handler持有activity或者fragment,handler又被message持有,message的target属性,message被messagequeue持有,messagequeue被looper中的threadlocal持有

java中匿名内部类会默认持有外部类的引用

打断持有链

  1. handler.removemesage handler.removecallbacks
  2. 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) {}

// enqueue
public 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;  
    }  
}

标签:boolean,next,Handler,msg,message,null,final,知道
From: https://www.cnblogs.com/android-lol/p/17410693.html

相关文章

  • 希望计算机专业同学都知道这些博主
    湖科大教书匠——计算机网络“宝藏老师”、“干货满满”、“羡慕湖科大”...这些都是网友对这门网课的评价,可见网课质量之高!最全面的面试网站湖南科技大学《计算机网络》微课堂是该校高军老师精心制作的视频课程,用简单的语言描述复杂的问题,用生动的动画演示抽象概念,更加便于学生......
  • 自动化测试基础知识,你知道的不知道的都在这里
    01自动化定义借助测试工具,依照测试规范,局部或全部代替人工测试,提高测试有效性。备注:测试不需要对软件所有功能进行测试,比如很多软件的帮助。02特征模拟人工操作:软件替代人工操作、对界面进行操作。03自动化测试特点·优化成本·可靠·快速·规范化·可重用04自动化......
  • 记录一次全局异常告警@ExceptionHandler和HandlerExceptionResolver的问题
         最近有同事说之前写的全局异常告警,如果有@Valid的注解,在接入新写的插件告警后,返回信息不打印了。全局异常是基于@ExceptionHandler的全局异常类,主要是ServletMVC的ModelAndView返回的错误信息的捕获。代码如下:   /***@authorxxx*/@RestControlle......
  • @ExceptionHandler注解
    1,基本使用方法Spring的@ExceptionHandler可以用来统一处理方法抛出的异常,比如这样:@ExceptionHandler()publicStringhandleExeption2(Exceptionex){System.out.println("抛异常了:"+ex);ex.printStackTrace();StringresultStr="异常:默认";returnr......
  • Android消息机制——Handler、Looper、MessageQueue
    最近在做毕设,关于android的,其中觉得android的消息机制很有意思,这里就写下自己的想法和Windows一样android也是消息驱动的。Android通过Handler和looper实现消息循环机制。一、Handler的创建每个Handler都会和一个线程和线程的messagequeue关联起来,此时你可以传递messages和runna......
  • Netty高手必知必会的ChannelHandlerContext技巧
    1概述ChannelHandlerContext代表ChannelHandler和ChannelPipeline之间的关联,每当有ChannelHandler添加到ChannelPipeline,都会创建ChannelHandlerContext。1.1主要功能管理它所关联的ChannelHandler和在同一个ChannelPipeline中的其他ChannelHandler之间的交互。Ch......
  • 5种你未必知道的JavaScript和CSS交互的方法
    随着浏览器不断的升级改进,CSS和JavaScript之间的界限越来越模糊。本来它们是负责着完全不同的功能,但最终,它们都属于网页前端技术,它们需要相互密切的合作。我们的网页中都有.js文件和.css文件,但这并不意味着CSS和js是独立不能交互的。下面要讲的这五种JavaScript和CSS共同合作的方......
  • Netty 爱好者必看!一文详解 ChannelHandler 家族,助你快速掌握 Netty 开发技巧!
    1Channel接口的生命周期Channel定义了一组和ChannelInboundHandlerAPI密切相关的简单但功能强大的状态模型1.1Channel的状态状态描述ChannelUnregisteredChannel已经被创建,但还未注册到EventLoopChannelRegisteredChannel已经被注册到了EventLoopChannelActiveChanne......
  • 员工不知道应该制定什么样的目标要怎么办?
    员工不知道应该制定什么样的目标时,可以考虑以下几点:点击免费领取OKR案例库、OKR整套学习视频等资料目标必须与组织和团队的能力最大程度所匹配。如果员工的目标过高或过低,都可能导致他们无法完成任务或丧失动力。目标应该具体明确,而且要用积极正向的语言去表达。比如,如果员工......
  • SpringBoot中@ControllerAdvice/@RestControlAdvice+@ExceptionHandler实现全局异常捕
    场景在编写Controller接口时,为避免接口因为未知的异常导致返回不友好的结果和提示。如果不进行全局异常捕获则需要对每个接口进行try-catch或其他操作。 可以对Controller进行全局的异常捕获和处理,一旦发生异常,则返回通用的500响应码与通用错误提示。并将异常发生的具体的......