Android Handler 解析

151 阅读6分钟

一:背景

最近行情不好,不幸被裁,老板耍赖不肯给全额赔偿,没办法只能准备面试,但是上家一天996,基本上没有学习到什么新的知识点,老的知识点又忘的差不多了。所以准备写一下对应知识点的文章,方便自己检查自己的掌握情况,也有利于自己记住这些知识点。

二:正文

handler已经是一个老生常谈的东西了,基本上大家都对它了解的比较多,本文尽量深入挖掘它的底层和不为人知的部分,错误之处还请大家指出

1.概述

image.png 上图是整个handler机制的运行流程图,简单概括各个类的作用就是:

  • handler:发送和处理消息
  • Message:消息对象,承载消息内容
  • MessageQueue:消息队列
  • Looper:消息轮询器,不断查询消息,取出消息

2.功能实现

Handler

构造方法:

public Handler(@Nullable 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 " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

handler的构造方法最终调用该方法,通过该方法可以看到,创建对象的时候获取到了looper对象,获取到了messagequeue。 点击查看Looper.myLooper()会发现是获取当前线程的ThreadLocal中的looper对象,所以在子线程中使用handler需要先Looper.prepare(),通过该方法创建当前线程的looper对象。然后调用Looper.loop使用过

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

让我们看看sThreadLocal.set(new Looper(quitAllowed));

public void set(T value) {

        //获取当前线程id
        Thread t = Thread.currentThread();

        //通过线程id获取它内部的map(threadLocals)(Thread类中维护了线程唯一的ThreadLocalMap)
        ThreadLocalMap map = getMap(t);

        //如果线程内部的map已经存在则直接调用去set,如果没有则创建一个。
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

可以看到把创建的looper对象设置给了当前线程 PS:主线程中之所以不需要调用 Looper.prepare() 是因为ActivityThread中的main方法中调用了Looper.prepareMainLooper(); 系统帮我们初始化了looper对象

发送消息:

通过调用handler的各种send或者post消息方法最终调用到了enqueueMessage,把消息放入looper中的messagequeue中,按执行时间排序,其中有异步消息也有同步消息,同步消息按时间排序,会受同步屏障影响,异步消息不受同步屏障影响,

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

处理消息

根据发送消息的代码可以得知,消息中的target对象是当前的handler对象,查阅Looper的loop方法可以看到

msg.target.dispatchMessage(msg);

可知是调用了handler的dispatchMessage方法。

/**
 * Handle system messages here.
 */
public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(@NonNull Message msg) {
}

根据代码可以看出如果有msg的callback是走handler的handleMessage方法,没有msg的callback再走handler的构造方法的callback。

Looper

looper主要讲一下它的loop方法,是怎么轮询的,为什么死循环不会导致ANR。

for (;;) {
   Message msg = queue.next(); // might block 阻塞处
   if (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 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;

   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);
       }
   }
   msg.recycleUnchecked();
}

以上就是loop方法中最关键的地方,可以看到死循环处理消息,同时queue.next()进行了阻塞。那么为什么没有导致ANR呢?

首先什么是ANR呢?ANR顾名思义就是应用程序无响应,纸面意思就是用户操作了,没有效应。ANR是Android系统主动抛出来的一个异常,不同的地方时间不同,输入事件后主线程5秒没响应就会抛出ANR。 这是因为这里用到了Linux系统的一个pipe和epoll机制。

  • pipe机制,在没有消息时阻塞线程并进入休眠释放cpu资源,有消息时唤醒线程
  • epoll机制,读端只会在写端写入数据时触发

比方说用户点击,此时就会产生一个点击事件被系统的屏幕管理服务捕获,再通过Binder传递给ApplicationThread,然后applicationthread就会通过handler发送消息给activitythread,此时messagequeue中就有消息,就不会堵塞在此会进行对应的逻辑处理

image.png

可以看到Loop方法中有这样一段代码

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;
   }

Observer是Looper中的接口,里面定义了一些方法,可以用来监听当前handler到底处理了那些message,用来解决一些疑难问题很有用处。

Message

消息分为:

  • 同步消息:按执行时间在messagequeue中排序,target为当前handler,
  • 异步消息:跟同步消息结构都一样,只是flags = FLAG_ASYNCHRONOUS;
  • 屏障消息:用在需要优先级高的场景,target为null,同时把自己放在队头,当target为null时,messagequeue会把异步消息放在前面执行

MessageQueue

获取下一个消息逻辑

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;
            }
            。。。。
           }
 }
 

同步屏障

场景用处:view绘制时使用,在ViewRootImpl中的scheduleTraversals发送同步屏障消息,确保view绘制消息能够及时执行,界面及时绘制,不会导致卡顿,同步屏障设置后需要调用解除同步屏障方法removeSyncBarrier

@UnsupportedAppUsage
void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}
//未设置 target
private int postSyncBarrier(long when) {
    // Enqueue a new sync barrier token.
    // We don't need to wake the queue because the purpose of a barrier is to stall it.
    synchronized (this) {
        final int token = mNextBarrierToken++;
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when;
        msg.arg1 = token;

        Message prev = null;
        Message p = mMessages;
        if (when != 0) {
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
        }
        if (prev != null) { // invariant: p == prev.next
            msg.next = p;
            prev.next = msg;
        } else {
            msg.next = p;
            mMessages = msg;
        }
        return token;
    }
}