android handler的理解

android handler的理解

在看handler源码前,我一直以为google构造handler的目的是方便开发者在其他线程中
调用执行主线程的方法或者在主线程中调用执行其他线程的方法。看完源码后我发现,原来整个app就是在handler机制下运行的,可以说handler是整个app的消息架构,很重
要。

我们平常使用最多的就是使用handler 的new Handler().postDelayed() 和new Handler().sendMessage()的方法,我们直接看new Handler().postDelayed() 的实现原理,看代码。

   public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }
    //将Runnable封装成Message
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

那么Message是什么呢?看代码。

public final class Message implements Parcelable {
    /**
     * User-defined message code so that the recipient can identify
     * what this message is about. Each {@link Handler} has its own name-space
     * for message codes, so you do not need to worry about yours conflicting
     * with other handlers.
     */
     //这个消息是什么
    public int what;

    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg1;

    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg2;

    /**
     * An arbitrary object to send to the recipient.  When using
     * {@link Messenger} to send the message across processes this can only
     * be non-null if it contains a Parcelable of a framework class (not one
     * implemented by the application).   For other data transfer use
     * {@link #setData}.
     *
     * <p>Note that Parcelable objects here are not supported prior to
     * the {@link android.os.Build.VERSION_CODES#FROYO} release.
     */
    public Object obj;

    /**
     * Optional Messenger where replies to this message can be sent.  The
     * semantics of exactly how this is used are up to the sender and
     * receiver.
     */
    public Messenger replyTo;

    /**
     * Indicates that the uid is not set;
     *
     * @hide Only for use within the system server.
     */
    public static final int UID_NONE = -1;

    /**
     * Optional field indicating the uid that sent the message.  This is
     * only valid for messages posted by a {@link Messenger}; otherwise,
     * it will be -1.
     */
    public int sendingUid = UID_NONE;

    /**
     * Optional field indicating the uid that caused this message to be enqueued.
     *
     * @hide Only for use within the system server.
     */
    public int workSourceUid = UID_NONE;

    /** If set message is in use.
     * This flag is set when the message is enqueued and remains set while it
     * is delivered and afterwards when it is recycled.  The flag is only cleared
     * when a new message is created or obtained since that is the only time that
     * applications are allowed to modify the contents of the message.
     *
     * It is an error to attempt to enqueue or recycle a message that is already in use.
     */
    /*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;

    @UnsupportedAppUsage
    /*package*/ int flags;

    /**
     * The targeted delivery time of this message. The time-base is
     * {@link SystemClock#uptimeMillis}.
     * @hide Only for use within the tests.
     */
    @UnsupportedAppUsage
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    //时间,跟延时有关
    public long when;

    /*package*/ Bundle data;

    @UnsupportedAppUsage
    //消息属于哪个handler,用于回调响应的事件。
    /*package*/ Handler target;

    @UnsupportedAppUsage
    //封装的runnabel,同样用于回调响应的事件。
    /*package*/ Runnable callback;

    // sometimes we store linked lists of these things
    @UnsupportedAppUsage
    //链表,下一个消息,与MessageQueue有关系
    /*package*/ Message next;

可见,Message说就是个可以序列化的普通对象,对象里面存储了what,target,next,callback等重要的属性。接上面。

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    
    public boolean sendMessageAtTime(@NonNull 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);
    }

这个mQueue是什么时候创建的呢?是在构造函数里,看源码。

 public Handler() {
        this(null, false);
    }

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

什么是Looper?看源码。

  static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
 /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

可见,Looper在全局创建了一个ThreadLocal对象,目的是通过该对象,在不同的线程中使用各自的Looper对象,为什么要使用Looper对象呢?从名字中就可以看出,这个对象可以维护一个循环,什么样的循环呢?

  */
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
		......
		//维护的这个循环
		 for (;;) {
		 	//取出message
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            .....
            try {
            	//将消息分发到handler回调需要响应的事件
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } 
         }
}

那这个线程(主线程)的looper是什么时候创建的呢?是在进程启动的时候,在ActiivtyThead的main函数中。

 public static void main(String[] args) {
	....
	 Looper.prepareMainLooper();
	 ....
	  if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

}

那MessageQueue queue 又是什么呢?看源码:

一个持有一系列message对象的类,这些message对象被Looper分发。
 Low-level class holding the list of messages to be dispatched by a
 * {@link Looper}.  Messages are not added directly to a MessageQueue,
 * but rather through {@link Handler} objects associated with the Looper.
 *
 * <p>You can retrieve the MessageQueue for the current thread with
 * {@link Looper#myQueue() Looper.myQueue()}.
 */
public final class MessageQueue {
	.....
	//meassageQueue中首个message
	 Message mMessages;
	
	  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;
        //重点,循环获取message
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
			//重点,通过linux提供的等待机制等待nextPollTimeoutMillis。为什么要调用这个函数呢?是因为我们在handler.setPostDelay中有设置的延迟时间。
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                //取出messageQueue中的首个message
                Message msg = mMessages;
                //如果该message没有事件要执行,继续按照message.next链表的方式取下一个
                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.
                        //返回一个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;
                }
    }
}

可以看出,messgeQueue主要是持有了一个Message对象,这个message通过链表关联了一系列message,messageQueue在获取消息的时候会从持有的message对象开始遍历,如果发现这个message的when设置的时间比当前时间晚,就调用nativePollOnce(ptr, nextPollTimeoutMillis)等待nextPollTimeoutMillis,从而达到了延时的目的。
那么问题来了,如果我们发送先发送一个Message设置演示5s,在发送一个消息设置延时1s,等执行第二个消息的时候发现时间已经过去了岂不是达不到延时的效果,其实messageQueue在添加message的时候为message的when设置了排序,延时短的被排列在链表的前面,看源码,接发送消息的时候enqueueMessage。

boolean enqueueMessage(Message msg, long when) {
        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
            Message p = mMessages;
            boolean needWake;
            //如果没有首message或延迟时间设置0,或者要插入的Message的延迟时间小于首message的延时时间,直接插入链表
            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;
                //开始遍历Message
                for (;;) {
                    prev = p;
                    p = p.next;
                    //如果插入Message的延时时间小于当前message的延迟时间,找到了,跳出循环
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                //将新的message出入message间隔之间
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
            	//有消息插入,唤醒messageQueue的next等待
                nativeWake(mPtr);
            }
        }
        return true;
    }

可见,Message的消息插入messageQueue是按照message的when来排序的。

在Looper的loop()函数中,会调用 msg.target.dispatchMessage(msg)将message分发给handler的dispatchMessage函数,看源码。

public void dispatchMessage(@NonNull Message msg) {
		//callback就是我们handler.post(Runnable run)的run.
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
        //mCallback只有一个boolean handleMessage(@NonNull Message msg)方法,可以在构造handler的时候设置。
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //回调handleMessage
            handleMessage(msg);
        }
    }
//执行Runnable的run方法。
 private static void handleCallback(Message message) {
        message.callback.run();
    }
    //我们经常设置message消息的事件回调。
  public void handleMessage(@NonNull Message msg) {
    }

handler消息机制的源码就分析完了,总结下,我们通过handler发送的消息是直接被封装成message对象,然后被messageQueue按照when的事件排序进行了链表封装。在Looper中会有一个死循环不停的从messageQueue中取出消息来回调message设置的回调事件,如果发现消息有延时,则等待到延时时间在投递。

那么问题来了,在主线程中这样一个死循环不会造成ANR吗?肯定是不会,原因我想有以下几点:
1 handler的等待用的nativePollOnce,等新的消息投递过来的时候再唤醒,有点类似于java的wait和nofity,可见,我们在android中任何的ui操作、屏幕刷新、按键触摸操作,肯定是会想主线程的messageQueue投递消息,让主线程去执行操作的。(ui操作、屏幕刷新、按键触摸操作这些的handler消息投递还没有找到源码证实,算是猜的)。activity、service等组件的生命周期是有handler消息投递实现的。
2 nativePollOnce在等待过程中会释放cpu资源,不会占用太多资源。可见,android就是一个基于handler的消息架构来运行的。

源码看完了,记录下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值