美文网首页
handler源码分析及手写handler架构

handler源码分析及手写handler架构

作者: Coder_Sven | 来源:发表于2019-08-14 17:04 被阅读0次

APP启动的入口在ActivityThread类中的main()方法

    public static void main(String[] args) {
        // 1. 为主线程创建1个Looper对象,同时生成1个消息队列对象(MessageQueue)
        // 方法逻辑类似Looper.prepare()
        Looper.prepareMainLooper();
        ...
        //创建了主线程
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
        ...
        //开始循环
        Looper.loop();
    }

再看看Looper源码中相关方法

   //Looper构造方法,绑定了一个线程并创建了一个消息队列MessageQueue
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
  
    public static void prepareMainLooper() {
        prepare(false);
        ...
        sMainLooper = myLooper();
        ...
    }
    
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //sThreadLocal是一个存储容器,里面存着线程中唯一一个Looper对象
        sThreadLocal.set(new Looper(quitAllowed));
    }    
    
    //取出来sThreadLocal中保存的唯一一个Looper对象
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    
    //循环取读取message
     public static void loop() {
        
        final Looper me = myLooper();      
        final MessageQueue queue = me.mQueue;
        ...

        for (;;) {//一个死循环
            //这是一个阻塞方法,当队列中没有消息时会一直阻塞
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ...

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            try {
                //==handler.dispatchMessage回调handler中重写的handlemessage方法
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ...

            msg.recycleUnchecked();
        }
    }  
    

现在来看看MessageQueue类的源码

   //消息出队,将消息移出消息队列
   Message next() {
        ...
        // 该参数用于确定消息队列中是否还有消息
        // 从而决定消息队列应处于出队消息状态 or 等待状态
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            //真正的阻塞方法,native层方法实现  nextPollTimeoutMillis =-1时代表一直阻塞
            nativePollOnce(ptr, nextPollTimeoutMillis);

            //下面的代码的作用是计算阻塞时间,延时加入消息队列时间等(msg.when)
            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) {
                    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;
                }

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

            }

            ...
        
        }
    }
    
     /**分析4:queue.enqueueMessage(msg, uptimeMillis)
          * 作用:入队,即 将消息 根据时间 放入到消息队列中(Message ->> MessageQueue)
          * 采用单链表实现:提高插入消息、删除消息的效率
           */
    boolean enqueueMessage(Message msg, long when) {
     
        //根据消息的延时时间,来将消息加入到队列中去
        synchronized (this) {
            ...
            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.
                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;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                //唤醒next()方法中的阻塞
                nativeWake(mPtr);
            }
        }
        return true;
    }    
    

Message类是一个使用了链表结构来实现的对象池(Message.next)

package android.os;

import android.os.MessageProto;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;

public final class Message implements Parcelable {

    public int what;


    public int arg1;

    public int arg2;

    public Object obj;

    public Messenger replyTo;

    public int sendingUid = -1;

    /*package*/ static final int FLAG_IN_USE = 1 << 0;

    /** If set message is asynchronous */
    /*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;

    /** Flags to clear in the copyFrom method */
    /*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;

    /*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 */
    public static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;//对象池大小

    private static final int MAX_POOL_SIZE = 50;

    private static boolean gCheckRecycle = true;

    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
    
    ...
    
    public long getWhen() {
        return when;
    }

    public void setTarget(Handler target) {
        this.target = target;
    }

    public Handler getTarget() {
        return target;
    }

    /*package*/ boolean isInUse() {
        return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
    }

    /*package*/ void markInUse() {
        flags |= FLAG_IN_USE;
    }

    public Message() {
    }
}

开始使用hanldler发送消息(handler.sendMessage())

   //构造方法
    public Handler() {
        this(null, false);
    }
   
    public Handler(Callback callback, boolean async) {
        ...
        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;
    }
  
   
   public final boolean sendMessage(Message msg)
    {
        // ->>分析1   
        return sendMessageDelayed(msg, 0);
    }
    
    //分析1:sendMessageDelayed(msg, 0)
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        // ->> 分析2
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
       
    //分析2:sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)      
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        ...
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    
    //分析3:enqueueMessage(queue, msg, uptimeMillis)
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)    {
        // 1. 将msg.target赋值为this
        // 即 :把 当前的Handler实例对象作为msg的target属性
        msg.target = this;
        ...
        // 2. 调用消息队列的enqueueMessage()
        // 即:Handler发送的消息,最终是保存到消息队列->>分析4
        return queue.enqueueMessage(msg, uptimeMillis);
    }

根据上面源码分析总结:

944365-494e0b26a2724087.png

常见问题分析

为什么不能在子线程中更新UI,根本原因是什么?

在访问UI时,ViewRootImpl会首先调用checkThread()方法检查当前线程是不是UI线程,如果不是UI线程就会抛出异常。那么为什么onCreate里面没有进行这个检查呢。这个问题原因出现在Activity的生命周期中,在onCreate方法中,UI处于创建过程,对用户来说界面还不可视,直到onStart方法后界面可视了,再到onResume方法后界面可以交互。从某种程度来讲,在onCreate方法中不能算是更新UI,只能说是配置UI,或者是设置UI的属性。这个时候不会调用到ViewRootImpl.checkThread(),因为ViewRootImpl没被创建。而在onResume方法后,ViewRootImpl才被创建。这个时候去交互界面才算是更新UI。
setContentView只是建立了View树,并没有进行渲染工作(其实真正的渲染工作是在
onResume之后)。也正是建立了View树,因此我们可以通过findViewById()来获取到View对象,但是由于并没有进行渲染视图的工作,也就是没有执行ViewRootImpl.performTransversal。同样View中也不会执行onMeasure(),如果在onResume()方法里直接获取View.getHeight()/View.getWidth()得到的结果总是0。

为什么主线程用Looper死循环不会引发ANR异常?

简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,
此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,
通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制。

为什么Handler构造方法里面的Looper不是直接new?

如果在Handler构造方法里面new Looper,怕是无法保证保证Looper唯一,只有用Looper.prepare()才能保证唯一性,具体去看prepare方法。

MessageQueue为什么要放在Looper私有构造方法初始化?

因为一个线程只绑定一个Looper,所以在Looper构造方法里面初始化就可以保证mQueue也是唯一的Thread对应一个Looper 对应一个 mQueue。

手写handler架构代码:
https://github.com/games2sven/Handler

相关文章

网友评论

      本文标题:handler源码分析及手写handler架构

      本文链接:https://www.haomeiwen.com/subject/rsmvjctx.html