Handler源码

Posted cao_null

tags:

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

Handler

public Handler() 
        this(null, false);
    


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());
            
        
           //调用了Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) //为空则抛异常
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        
        //获取MessageQueue
        mQueue = mLooper.mQueue;
        mCallback = callback;
        //异步先按住不表
        mAsynchronous = async;
    

Looper

Looper如此关键,没有就crash,看下Looper生成的过程

public static @Nullable Looper myLooper() 
        return sThreadLocal.get();//ThreadLocal保证每个线程都有独立的Looper


//创建过程默认能退出
public static void prepare() 
        prepare(true);



private static void prepare(boolean quitAllowed) 
        //一个线程只有一个Looper
        if (sThreadLocal.get() != null) 
            throw new RuntimeException("Only one Looper may be created per thread");
        
        sThreadLocal.set(new Looper(quitAllowed));



private Looper(boolean quitAllowed) 
        //创建MessageQueue
        mQueue = new MessageQueue(quitAllowed);

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


单独分析主线程也就是UI线程创建Looper过程

//主线程创建sMainLooper
public static void prepareMainLooper() 
        prepare(false);
        synchronized (Looper.class) 
            if (sMainLooper != null) 
                throw new IllegalStateException("The main Looper has already been prepared.");
            
            sMainLooper = myLooper();
        



//ActivityThread
public static void main(String[] args) 
        ...
        //创建主线程Looper
        Looper.prepareMainLooper();

        // Find the value for @link #PROC_START_SEQ_IDENT if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) 
            for (int i = args.length - 1; i >= 0; --i) 
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) 
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                
            
        
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) 
            sMainThreadHandler = thread.getHandler();
        

        if (false) 
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        //启动looper
        Looper.loop();
        //无限循环走到这说明出异常了
        throw new RuntimeException("Main thread loop unexpectedly exited");
    
/**
     * 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) //必须先prepare
            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();

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

        boolean slowDeliveryDetected = false;
        //死循环
        for (;;) 
            Message msg = queue.next(); // might block
            if (msg == null) 
                //没消息说明正在推出
                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);
            

            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;
            try 
                //分发
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
             finally 
                if (traceTag != 0) //trace相关
                    Trace.traceEnd(traceTag);
                
            
            if (logSlowDelivery) 
                if (slowDeliveryDetected) 
                    if ((dispatchStart - msg.when) <= 10) 
                        Slog.w(TAG, "Drained");
                        slowDeliveryDetected = false;
                    
                 else 
                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                            msg)) 
                        // Once we write a slow delivery log, suppress until the queue drains.
                        slowDeliveryDetected = 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);
            
            //回收message
            msg.recycleUnchecked();
        

MessageQueue和Message

public final class Message implements Parcelable //继承自Parcelable


    public int what;

    public Object obj;

    //下面的是访问权限是包内
    /*package*/ int flags;

    /*package*/ long when;

    /*package*/ Bundle data;

    /*package*/ Handler target;

    /*package*/ Runnable callback;

    // sometimes we store linked lists of these things
    /*package*/ Message next;

    /** @hide */
    //下面是static修饰的
    public static final Object sPoolSync = new Object();//同步锁
    private static Message sPool;
    private static int sPoolSize = 0;

    private static final int MAX_POOL_SIZE = 50;//数据池最大size

    public Message() 

    
    
    //生成Message的时候最终都是调用obtain
    public static Message obtain() 
        synchronized (sPoolSync) 
            if (sPool != null) 
                Message m = sPool;
                sPool = m.next;//返回当前sPool,然后挪动next指针
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            
        
        return new Message();//如果超过了就重新new
    

    //这是回收
    public void recycle() 
        if (isInUse()) 
            if (gCheckRecycle) 
                throw new IllegalStateException("This message cannot be recycled             because it "
                        + "is still in use.");
            
            return;
        
        recycleUnchecked();
    

    /**
     * Recycles a Message that may be in-use.
     * Used internally by the MessageQueue and Looper when disposing of queued Messages.
     */
    void recycleUnchecked() 
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) 
            if (sPoolSize < MAX_POOL_SIZE) //小于最大容量
                next = sPool;//挪动指针
                sPool = this;
                sPoolSize++;
            
        
    

这里我们知道了message的数据结构是通过next串联起来的,并且最好使用obtain而避免自己new,可以提高性能。

然后是MessageQueue

public final class MessageQueue 


    //能否退出
    private final boolean mQuitAllowed;

    @SuppressWarnings("unused")
    private long mPtr; //native用的

    Message mMessages;
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
    private IdleHandler[] mPendingIdleHandlers;
    private boolean mQuitting;

    // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
    private boolean mBlocked;

    // The next barrier token.
    // Barriers are indicated by messages with a null target whose arg1 field carries the token.
    private int mNextBarrierToken;

    //下面这些都是native方法,暂时不深究
    private native static long nativeInit();
    private native static void nativeDestroy(long ptr);
    private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
    private native static void nativeWake(long ptr);
    private native static boolean nativeIsPolling(long ptr);
    private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);

    MessageQueue(boolean quitAllowed) 
        //主线程不允许退出,子线程允许
        mQuitAllowed = quitAllowed;
       //记录消息队列
        mPtr = nativeInit();
    

next方法信息量比较大

Message next() 
        final long ptr = mPtr;
        if (ptr == 0) // message的loop已经退出而且执行了disposed.
            return null;
        

        int pendingIdleHandlerCount = -1; // 只有第一次是-1
        int nextPollTimeoutMillis = 0;
        for (;;) //又是一个死循环
            if (nextPollTimeoutMillis != 0) 
                Binder.flushPendingCommands();
            
            //阻塞方法,会被wake唤醒,也可以在nextPollTimeoutMillis时间后唤醒
            //nextPollTimeoutMillis是0立即返回,-1就阻塞,否则等到nextPollTimeoutMillis时间后返回
            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) 
                    //这里target是null,说明遇到了barrier
                    do 
                        prevMsg = msg;//如果执行了这里
                        msg = msg.next;
                     while (msg != null && !msg.isAsynchronous());//找到下一个异步消息为止
                
                if (msg != null) 
                    if (now < msg.when) 
                        // 下个消息时间还没到,设置等待时间
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                     else 
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) //看上面执行了prevMsg赋值,说明找到了异步消息,则重新链接message表
                            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.没信息重置为1
                    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();
                
                //如果依然是-1说明不能执行IdleHandler,所以block,跳出循环,如果nextPollTimeoutMillis也是-1则回到之前的nativepullonce
                if (pendingIdleHandlerCount <= 0) 
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                
                //初始化最少4个handlers
                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();//执行具体的queueIdle
                 catch (Throwable t) 
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                

                if (!keep) //如果返回true则remove,否则无限循环
                    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.
            //有可能有普通消息过来,直接设置0,不等待进入下次循环
            nextPollTimeoutMillis = 0;
        
    

看完了next出列,接下来看enqueueMessage入列。

boolean enqueueMessage(Message msg, long when) 

        //两个必要的检查,为什么必须有target?刚才next还有没有target的情况,晚点再返回来看
        if (msg.target == null) 
            throw new IllegalArgumentException("Message must have a target.");
        
        if (msg.isInUse()) 
            throw new IllegalStateException(msg + " This message is already in use.");
        
        

        synchronized (this) 
            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) //之前没有消息或者已经到了触发时间
                //新入列的插入头部
                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.
                //是否唤醒需要看target异步消息
                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;
            

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) 
                nativeWake(mPtr);//唤醒
            
        
        return true;
    

接下来看一下同步屏障是什么

public int postSyncBarrier() 
        return postSyncBarrier(SystemClock.uptimeMillis());//同步屏障是隐藏方法,且只支持当前时间
    

    private int postSyncBarrier(long when) //这里的message没有target
        // 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;
        
    

然后是移除同步屏障

public void removeSyncBarrier(int token) 
        // Remove a sync barrier token from the queue.
        // If the queue is no longer stalled by a barrier then wake it.
        synchronized (this) 
            Message prev = null;
            Message p = mMessages;
            while (p != null && (p.target != null || p.arg1 != token)) 
                prev = p;
                p = p.next;
            
            if (p == null) //没找到就崩溃
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            
            final boolean needWake;
            if (prev != null) 
                prev.next = p.next;
                needWake = false;//之前还有其他消息不唤醒
             else 
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;//看当前消息再决定是否唤醒
            
            p.recycleUnchecked();//回收

            // If the loop is quitting then it is already awake.
            // We can assume mPtr != 0 when mQuitting is false.
            if (needWake && !mQuitting) 
                nativeWake(mPtr);
            
        
    

小结:

next的时候,假设有同步屏障,接下来只取异步消息,同步消息全部等待,来保证异步消息尽快执行。查看调用,使用同步屏障的地方主要在编舞者等系统关键时刻。消息的入队和出队在这基本分析完了,接下来是事件分发。

消息分发

Looper中调用了msg.target.dispatchMessage,具体看下

public void dispatchMessage(Message msg) 
        if (msg.callback != null) //先判断msg是否有callback,有就执行handleCallback
            handleCallback(msg);
         else 
            if (mCallback != null) //否则调用handler的callback
                if (mCallback.handleMessage(msg)) 
                    return;
                
            
            handleMessage(msg);//没人处理走这
        
    


/**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) 

    

还有时间发送的post

public final boolean post(Runnable r)
    
       return  sendMessageDelayed(getPostMessage(r), 0);
    


public final boolean postAtTime(Runnable r, long uptimeMillis)
    
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    


public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
    
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    


//最终都执行这个,传入两个必备的参数,Message和时间
public boolean sendMessageAtTime(Message msg, long uptimeMillis) 
        MessageQueue queue = mQueue;
        if (queue == null) 
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        
        return enqueueMessage(queue, msg, uptimeMillis);
    

总结

分工

Message:消息本身,存储一些target,data之类的数据

MessageQueue:消息队列主要维护消息,取出next或者插入数据enqueueMessage,也维护同步屏障异步消息等逻辑。

Looper:维持线程和Looper循环对应关系,分发到对应的msg。

Handler:负责发送和处理最终事件逻辑。

系统UI相关

同步屏障:加入同步屏障后,同步消息全部阻塞,异步消息能正常执行。

异步消息:可以理解为紧急消息,避免因为同步消息过多导致执行不及时。

使用在编舞者等系统UI关键操作中,避免消息过多导致系统关键操作卡顿。

本地方法

nativePollOnce:维持主线程不退出,并且阻塞逻辑,不占用CPU资源

nativeWake:适当时机唤醒循环

更多native方法逻辑,后续整理再发

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

Android Handler那些事儿,消息屏障?IdelHandler?ANR?

Android Handler 机制 屏障消息(同步屏障)

Handler同步屏障

Handler sync barrier

什么是Handler的同步屏障机制?

(4.1.10.8)Android Handler之同步屏障机制(sync barrier)