Handler小记

基本使用

Handler handler = new Handler();

Message message = handler.obtainMessage(); // 或者 Message message = Message.obtain() 

handler.send(message); // 基本

handler.post(new Runable() {
  @Overriable
  public void run() {
      // do ...
  }
})


源码流程

在这里插入图片描述

Handler 机制主要涉及四个主要部分:

  • Handler 负责消息收发处理代理,或者处理代理设置
  • Message 消息结构内容,如果为Runnable,最终也将转化为 Message.callback,另外Message提供了资源池,提供于反复利用。
  • Looper 负责便利消息队列(MessageQueue.next())进行便利,并且根据对应的Handler进行分发(Message.target)
  • MessageQueue

在这里插入图片描述

通常我们通过 Handler 就可以实现大部分功能的使用,在Handler 中,同时封装了 mLoopermQueue ,而 mQueue来源于 mLooper

    public Handler(@Nullable Callback callback, boolean async) {
        ...
        // 从当前线程中获得 Looper, 注意如果当前线程非主线程,则需要在当前线程提前使用 Looper.prepare() 以产生 Looper 对象,否则将报错 Only one Looper may be created per thread
        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;
    }

post

分析代码通常会从使用上入手,这里我们从简单的Handler.post(Runnable r)开始分析。

Handler.post(Runnable r)

post 主要是对sendMessage 的参数包装方法。
通过getPostMessageRunnable 参数进行的包装,将Runnable 参数作为Message.callback参数,转化为Message对象,最终又调用enqueueMessage 方法,将Message作为参数传入。

   private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

包装有 Runnable 参数的 Message,最终进入该方法,并且将Message添加到 MessageQueue 中(MessageQueue.enqueueMessage(Message, time)),至此Message进入MessageQueue 队列中,等待被调用执行。

// 比较简单,只是为了整合一些内容
    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);
    }

enqueueMessage(MessageQueue queue, Message msg,long uptimeMillis)

该方法是将消息添加到 MessageQueue 的链表 mMessages 中,并决定是否激活Looper

 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 p = mMessages; // 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 {
                // 主要是为了将消息根据when插入到 消息链合适的位置
                // 如果已经被阻塞并且队头是阻塞消息,当前消息是异步的情况 则重新唤醒looper
                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; 
                prev.next = msg;
            }

            if (needWake) {
                // 根据情况去激活Looper
                nativeWake(mPtr);
            }
        }
        return true;
    }

nativeWake(long ptr)

以下借用下别人的图,对于JNI这块还不太熟悉,简单记录下JNI下发生的内容。

在这里插入图片描述

nativeWake 是一个native 方法,映射于 android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr)

static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
    // 将Java层传递下来的mPtr转换为nativeMessageQueue
    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
    //调用wake函数
    nativeMessageQueue->wake();
}

主要也是负责执行 wake()

void NativeMessageQueue::wake() {
    mLooper->wake();
}

执行 Looper::wake()

void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
    ALOGD("%p ~ wake", this);
#endif

    uint64_t inc = 1;
    // 向管道mWakeEventFd写入字符1
    // TEMP_FAILURE_RETRY 是一个宏定义,当执行write失败后,会不断重复执行,直到执行成功为止。
    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
    if (nWrite != sizeof(uint64_t)) {
        if (errno != EAGAIN) {
            ALOGW("Could not write wake signal, errno=%d", errno);
        }
    }
}

到这里为止 Post 的流程结束,而Send流程其实和 Post差不多。

Main Handler

在之前中,我们提到一个Looper是需要在当前线程总调用 Looper.perpare 去产生的。但是在为什么主线程中,不需要调用也能执行。

这涉及到App的启动流程,在一个App启动后,会经过AMS到最后的ActivityThread,在这过程中,会调用ActivityThread.main() 去启动创建一个 ActivityThread 同时创建了Main线程的Looper。

    public static void main(String[] args) {
        ...

        Looper.prepareMainLooper();

        ...
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

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

        ...
        Looper.loop();
        ...
    }
   public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    
    ...
     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));
    }

在这里插入图片描述

为什么不会发生ANR

前面解析到如果在一个MessageQueue 中没有消息的话,那么Looper将进入阻塞,那么为什么不会产生ANR呢。

在Android主线程中,MainLooper 承担着UI绘制、触摸事件、键盘事件等一切可以想得到的主线程工作内容,都是通过MainLooper去处理。

在这里面光一个UI刷新则已经让MainLooper每一秒都停不下来(60ms标准),更何况很难再6s内阻塞,并且及时发生了,那么UI一定也卡住了,这已经是明显的ANR了。

Asynchronous 和 SyncBarrier

在我们分析中,应该已经发现了,在Message和Looper 等中会不断出现 Asynchronous 这一属性或者判断。

这里需要结合 SyncBarrier 一起说明,在Handler中消息分为异步消息、同步消息和阻塞消息(普通用户没办法直接创建),一般情况下,我们创建的消息都是同步消息,这里SyncBarrier 的作用主要体现在对同步消息和异步消息的处理态度。

SyncBarrier 也是Message的一种,只是该Message的 target=null(Message的target通常是发送它的Handler),对于该种消息的作用,体现在 MessageQueue 的 next() 中

Message next() {
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        ...

            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                // msg.target == null 说明当前队头消息被设置了SyncBarrier
                if (msg != null && msg.target == null) {
                    // 找到该消息后面的异步消息(msg.isAsynchronous()方法)
                    do {
                    	// 标识前一个消息
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                // 这时候得到的msg 或者是异步消息,或者是队头消息(非SyncBarrier消息) 或者是最后一个null消息
                if (msg != null) {
                    if (now < msg.when) {
                        // 还未到消息执行时间,则阻塞这段时间
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        mBlocked = false;
                        // prevMsg不为空,从队列中删除即将执行的msg,但是队头保持不变,即在下一个循环中,队头还是个空消息
                        // 从这里可以看出,如果被设置了SyncBarrier,只有当SyncBarrier被移除的时候,才会执行队列后面的非异步消息
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                        	// 替换队头
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // 队列中没有可用消息
                    nextPollTimeoutMillis = -1;
                }
            }
        }
    }

因此可以理解为,如果在消息列表读取中发现有同步阻塞消息,则阻塞下面的在它之后的消息执行(被读取到),除非该消息设置了 Asynchronous。

SyncBarrier的应用

同步阻塞通常用在垂直同步的UI刷新中,在ViewRootImpl.scheduleTraversale()中。

...
final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
}
...
void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            // 设置同步阻塞
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            // 等待下一个vsync信号会回调mTraversalRunnable
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

 // 该方法在接收到vsync信号时调用,执行view的measure、layout和draw
	void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            // 移除同步阻塞
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            performTraversals();
        }
    }    

这样子的处理是为了在执行UI同步时,将UI刷新优先置为最高优先级,延后其他消息的处理,提高流畅度。

IdleHandler

IdleHandler 用于在 Handler 线程空闲的时候(未接受到需要处理的Message时)进行的额外处理。

    public static interface IdleHandler {
        // 当消息内队列所有的Message都被执行完或者还有消息但是暂时还不需要执行(定时消息)时,该方法会被调用,当返回true,则IdleHandler会保留在消息队列中,否则执行完后将从队列中移除了
        boolean queueIdle();
    }

调用

 MessageQueue messageQueue = Looper.myQueue();
    messageQueue.addIdleHandler(new MessageQueue.IdleHandler() {
        @Override
        public boolean queueIdle() {
            ...
            return false;
        }
    });
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值