我所知道的Handler

Posted 真菜啊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我所知道的Handler相关的知识,希望对你有一定的参考价值。

简单讲,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;  
      

Android消息机--handler的自我总结

  大家都知道Handler是Android为了能在子线程更新UI所创作出来的一个类.

那么为什么Handler能在handler里面能更新UI。是因为Handler 把更新UI的操作切换到了主线程来操作.

有些博客说Handler是线程之间的一种通讯方式,这只局限于子线程跟主线程之间,

那么子线程是怎么把消息传送到主线程来操作,这其中一个关键的地方就是Looper.

从例子我们可以知道 假如现在有一段主要的代码


oncrete(){

new Thread(new Runnable(){


run{

//进行UI操作  ---这样是会报错的 那么我们加上handler


// 之前是没有的,现在加上Looper 

Looper.prepare();

new Handler(){

handlerMessage(){

//进行UI操作 --- 

// 如果是这样依然会报错 因为子线程中现在还没有Looper,会报没有Looper那个错误信息(从源码可以看到加了Looper检测)  那么我们加上Looper

}         

}

Looper.loop();


}


}).start;


}


从上面的代码中 加了Looper之后还是会报错,报应该在主线程中执行UI操作.

那么是为什么,原因在于 这个Looper不是主线程的Looper 

一个解决方法是 把Looper.prepare();改成Looper.getMainLooper();

或者 new Handler(Looper.getMainLooper);

这样就可以更新UI不会报错了,

那么Looper是怎样把更新UI的操作切换到主线程 

我们从源码可以看到 当我们new Handler(Looper.getMainLooper())的时候 里面有

Handler(Looper looperCallback callbackasync) {
    = looper= looper.mQueue= callback= async}

所以说这个时候 handler已经拿到了主线程的Looper;

然后我们在Looper的loop();方法里面看到

{
    msg.target.dispatchMessage(msg)} {
    (traceTag != ) {
        Trace.(traceTag)}
}

会调用msg.target 这个msg.target是在这里拿到

Looper me = ()(me == ) {
    RuntimeException()}
MessageQueue queue = me.mQueue
() {
    Message msg = queue.next()(msg == ) {
        }

在loop()方法里面Looper会开始无限循环 去处理信息啦。

顺序是以下这样

1:实例化Activity 的时候创建ActivityThread(主线程) 然后在Main()里面创建Looper 然后Looper里面通过mThreadLocal 的set方法保存主线程 

然后再调用Looper.loop();这时候会开启无限循环

2:Looper.getMainLooper 会调用get方法 这个时候就拿到了主线程的Looper 以及里面保存的messageQueue 

然后 handler调用post 其实也就是在主线程的messageQueue上插入一个信息

那么loop 方法里面就检测到了,这时候已经是切换到主进程了.


看源码比较能清晰整个流程


以上是关于我所知道的Handler的主要内容,如果未能解决你的问题,请参考以下文章

默认线程,如 DestroyJavaVM、Reference Handler、Signal Dispatcher

ajax调用handler,使用HttpWebRequest访问WCF服务

这些关于 Handler 的知识点你都知道吗?

Handler系列之原理分析

Android消息机--handler的自我总结

获取最后一个 id PDO