带你真正攻克Handler

先来一个本身画的Handler机制总体流程图,本文不会带着你走一遍源码,只会对重点须要注意的地方以及一些细节的处理作出解释,让你更好的了解Handler机制总体的运做bash

在这里插入图片描述

  • Handler经过sendMessage()发送Message到MessageQueue队列;
  • Looper经过loop(),不断提取出达到触发条件的Message,并将Message交给target来处理;
  • 通过dispatchMessage()后,交回给Handler的handleMessage()来进行相应地处理。
  • 将Message加入MessageQueue时,处往管道写入字符,能够会唤醒loop线程;
  • 若是MessageQueue中没有Message,并处于Idle状态,则会执行IdelHandler接口中的方法,每每用于作一些清理性地工做。

下边放几个须要注意的Handler知识点:

  1. Handler 的背后有 Looper、MessageQueue 支撑,Looper 负责消息分发,MessageQueue 负责消息管理;
  2. 在建立 Handler 以前必定须要先建立 Looper;
  3. Looper 有退出的功能,可是主线程的 Looper 不容许退出;
  4. 异步线程的 Looper 须要本身调用 Looper.quit(); 退出;
  5. Runnable 被封装进了 Message,能够说是一个特殊的 Message;
  6. Handler.handleMessage() 所在的线程是 Looper.loop() 方法被调用的线程,也能够说成 Looper 所在的线程,并非建立 Handler 的线程,Handler新建时持有的Looper在哪一个线程,最后Handler.handleMessage()就在哪一个线程执行
  7. 使用内部类的方式使用 Handler 可能会致使内存泄露,即使在 Activity.onDestroy 里移除延时消息,必需要写成静态内部类;

为何须要使用Handler

由于Android系统不容许在非UI线程更新UI,由于若是多个线程同时改变View的状态会形成最终View状态的不肯定性,若是给每一个View的操做都上锁的话那么势必会形成性能的损耗,因此干脆规定只能在UI线程去更新UI,而Handler就是用来进行线程切换操做的。 使用方法框架

class LooperThread extends Thread {
    public Handler mHandler;


    public void run() {
        Looper.prepare();  
        mHandler = new Handler() {  
            public void handleMessage(Message msg) {
                //TODO 定义消息处理逻辑.
            }
        };
        Looper.loop();  
    }
}
复制代码

主线程中可使用Handler的缘由是在ActivityThread中程序的入口main方法中调用了Looper.prepare();和Looper.loop();异步

Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
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.loop();
复制代码

Looper

  • Looper.prepare() Looper.prepare()在每一个线程只容许执行一次,该方法给当前线程经过TL绑定一个线程所属的惟一一个实例。
private static void prepare(boolean quitAllowed) {
    //看当前线程是否已经过TL绑定对应的实例,有的话抛异常,因此prepare方法只容许调用一次
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //建立Looper对象,并经过TL创建与线程的绑定关系
    sThreadLocal.set(new Looper(quitAllowed));
}
复制代码
  • ThreadLocal 咱们看一下ThreadLocal.set方法
public void set(T value) {
   Thread t = Thread.currentThread();//获取当前线程
   ThreadLocalMap map = getMap(t);//获取当前线程所属的ThreadLocalMap实例,键值对结构
   if (map != null)
       map.set(this, value); //以当前ThreadLocal做为键,Looper做为值创建绑定关系
   else
       createMap(t, value);
   }
}
复制代码

ThreadLocal.get方法async

public T get() {
    Thread t = Thread.currentThread();//获取当前线程
    ThreadLocalMap map = getMap(t);//获取当前线程所属的ThreadLocalMap实例,键值对结构
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);//经过当前ThreadLocal做为键取出对应的值
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}
复制代码

为何要选择ThreadLocal创建绑定关系?

由于咱们是要让每个线程都有且只有一个惟一的Looper实例,这时就可使用ThreadLocal给每一个线程绑定一个惟一实例的特性很方便的创建绑定关系。若是不采用ThreadLocal去实现,那么只能使用一个LooperManager管理类而后经过其中的Map去统一管理,那么这样无疑是很麻烦的。ThreadLocal只是做为主键,若是是Thread做为主键,那么很显然一个线程只能与一个对应的对象创建绑定关系,这显然是很是不合理的。ide

  • Looper.loop() loop()进入循环模式,主要进行了以下几点:
    1. 获取当前线程的Looper实例
    2. 经过Looper获取MessageQueue实例
    3. 开启死循环并在其中调用MessageQueue的next方法不断轮询MessageQueue的头结点
public static void loop() {
    final Looper me = myLooper();  //获取TLS存储的Looper对象 -->sThreadLocal.get()
    final MessageQueue queue = me.mQueue;  //获取Looper对象中的消息队列


    Binder.clearCallingIdentity();
    //确保在权限检查时基于本地进程,而不是调用进程。
    final long ident = Binder.clearCallingIdentity();


    for (;;) { //进入loop的主循环方法
        Message msg = queue.next(); //可能会阻塞 
        if (msg == null) { //没有消息则退出循环,调用Looper.quit()方法后返回空的message,随即退出
            return;
        }

        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        msg.target.dispatchMessage(msg); //用于分发Message 
        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        final long newIdent = Binder.clearCallingIdentity();
        msg.recycleUnchecked(); 
    }
}
复制代码
  • Looper.quit() 用于终止loop循环,主线程中不容许调用(能调用的话至关于主程序退出了,应用就直接挂掉了),子线程中要退出时须要主动调用,不然会形成子线程中一直处于死循环状态没法退出。调用后会改变MessageQueue中的mQuitting标志位,next方法中若是检测到mQuitting为true则直接返回null,loop方法中检测到message是null则直接return终止死循环从而结束逻辑使得线程能够退出。
public void quit() {
    mQueue.quit(false); //所有消息移除
}


public void quitSafely() {
    mQueue.quit(true); //只移除没有执行的消息
}
复制代码

如何作到延迟发送消息

  • 会根据Message发送时的时间戳肯定Message在MessageQueue中的位置。函数

    1. 放入Message时会根据msg.when这个时间戳进行顺序的排序,若是非延迟消息则msg.when为系统当前时间,延迟消息则为系统当前时间+延迟时间(如延迟发送3秒则为SystemClock.uptimeMillis() + 3000)
    2. 将Message放入MessageQueue时会以msg.when对msg进行排序确认当前msg处于单链表中的位置,分为几种状况: (1)头结点为null(表明MessageQueue没有消息),Message直接放入头结点。 (2) 头结点不为null时开启死循环遍历全部节点,退出死循环的条件是: 1.遍历出的节点的next节点为null(说明当前链表已经遍历到了末尾,将放入的Message放入next节点). 2.遍历出的节点的when大于放入message的when(说明当前message是一个比放入message延迟更久的消息,将放入的Message放入当前遍历的Message节点以前).
  • 当咱们发送消息的时候其实最后都会调用到sendMessageAtTime这个方法,这个方法其实最终会把你的Handler对象赋值给Message实体,咱们最终发送消息都是发送的Message实体,而后调用MessageQueue的enqueueMessage方法oop

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

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

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码
  • MessageQueue 是一个单链表结构,其中的Message节点是以Message放入MessageQueue的时间去进行顺序肯定的(小的在前大的再后),这样就完成了消息的延迟发送
boolean enqueueMessage(Message msg, long when) {
    // 每个普通Message必须有一个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) {  //正在退出时,回收msg,加入到消息池
            msg.recycle();
            return false;
        }
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) { //p为null(表明MessageQueue没有消息) 或者msg的触发时间是队列中最先的, 则进入该分支并将加入的message放入头结点
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked; //当阻塞时须要唤醒
        } else {
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {                             //开启死循环遍历message
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {  //退出条件为当前message的下一个节点为null或者当前节点的message执行时间大于你放入message的执行时间
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p;                           //进行赋值
            prev.next = msg;
        }
        //消息没有退出,咱们认为此时mPtr != 0
        if (needWake) {                        
            nativeWake(mPtr);                        //往管道中写数据,唤醒阻塞,nativePollOnce方法阻塞解除
        }
    }
    return true;
}
复制代码

MessageQueue的机制

  • Message的入列和出列实际上是一个很典型的**生产者-消费者模型**,其中使用了epoll机制,当没有消息的时候会进行阻塞释放CPU时间片避免死循环形成性能的浪费。虽然是不断循环取出头结点的Message进行分发处理可是若是没有消息时它是阻塞在 nativePollOnce这个native方法中的当咱们enqueue插入Message时会触发nativeWake这个方法去唤醒,从而nativePollOnce阻塞解除继续遍历MessageQueue取出头结点去处理。
  • Looper.loop()在一个线程中调用next()不断的取出消息,另一个线程则经过enqueueMessage向队列中插入消息,因此在这两个方法中使用了synchronized (this) {}同步机制,其中this为MessageQueue对象,无论在哪一个线程,这个对象都是同一个,由于Handler中的mQueue指向的是Looper中的mQueue,这样防止了多个线程对同一个队列的同时操做(如增长的同时正在轮询获取Message,有可能形成MessageQueue中最终结果的不肯定性)。
Message next() {
    final long ptr = mPtr;
    if (ptr == 0) { //当消息循环已经退出,则直接返回
        return null;
    }
    int pendingIdleHandlerCount = -1; // 循环迭代的首次为-1
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        //阻塞操做,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会返回
        //nextPollTimeoutMillis 为-1,一直阻塞,在调用nativeWake(enqueue Message或Looper.quit()退出Looper)时会被唤醒解除阻塞
        //nextPollTimeoutMillis 为0,不阻塞
        //nextPollTimeoutMillis 为>0,阻塞到对应时间后解除,如为10000则阻塞十秒后解除,用于处理延迟消息
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                //当消息Handler为空时,查询MessageQueue中的下一条异步消息msg,则退出循环。
                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 {
                    // 获取一条消息,并返回
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    //设置消息的使用状态,即flags |= FLAG_IN_USE
                    msg.markInUse();
                    return msg;   //成功地获取MessageQueue中的下一条即将要执行的消息
                }
            } else {
                //没有消息,阻塞
                nextPollTimeoutMillis = -1;
            }
            //消息正在退出,返回null
            if (mQuitting) {
                dispose();
                return null;
            }
            //当消息
复制代码

Message分发的三个的优先级

当遍历出Message后Message会获取其中的Handler并调用Handler的dispatchMessage进行分发,这时也会有三个优先级。post

  1. Message的回调方法:message.callback.run(),优先级最高; 对应handler.post(new Runnable)的方式发送消息
  2. Handler的回调方法:Handler.mCallback.handleMessage(msg),优先级仅次于1; 对应新建Handler时传进CallBack接口 Handler handler=new Handler(new Handler.Callback()....(一般咱们能够利用 Callback 这个拦截机制来拦截 Handler 的消息,场景如:Hook ActivityThread.mH,在 ActivityThread 中有个成员变量 mH ,它是个 Handler,又是个极其重要的类,几乎全部的插件化框架都使用了这个方法。)
  3. Handler的默认方法:Handler.handleMessage(msg),优先级最低。 对应新建Handler并复写handleMessage方法
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {             //callback是一个runnable对象
        handleCallback(msg);
    } else {
        if (mCallback != null) {            //mCallback是新建Handler时传进去的Callback接口
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);                //默认空实现,通常咱们会本身复写实现这个方法
    }
}

private static void handleCallback(Message message) {
    message.callback.run();
}
复制代码
  1. 将 Runnable post 到主线程执行(不少第三方框架都使用的这种方式方便的完成主线程的切换,这也是为何有handler.post(new Runnable)这种方式去发送消息)。
  2. 利用 Looper 判断当前线程是不是主线程。
public final class MainThread {
    private MainThread() {
    }

    private static final Handler HANDLER = new Handler(Looper.getMainLooper());

    public static void run(@NonNull Runnable runnable) {
        if (isMainThread()) {
           runnable.run();
        }else{
            HANDLER.post(runnable);
        }
    }

    public static boolean isMainThread() {
        return Looper.myLooper() == Looper.getMainLooper();
    }

}
复制代码

Handler线程调度的实质

Handler的实质其实就是共享内存,咱们看一个例子。性能

public class Demo {
    List mList= new ArrayList()<Message>;
    public static void main(String[] args) {
        //子线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000l);
                    mList.add(new Message());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }


            }
        }).start();
        //主线程开启死循环不断遍历list取头结点
        for (;;) {
            //主线程中处理
            Message message = mList.get(0);
            if (message != null) {
                //处理
            }
        }

    }
}
复制代码

咱们为了将数据最终从子线程切换到主线程中去其实只要拿到mList这个实例便可,这个mList对应的其实就是MessageQueue,而咱们要获取MessageQueue只要获取对应的Looper便可,当咱们Handler新建的时会根据Handler所在线程获取到其线程正在轮询消息的Looper对象,Handler中的mQueue指向的是其所在线程的Looper中的mQueue(固然也能够手动指定一个其余线程的Looper,不指定的话默认为当前线程的Looper),由此即可在发送Message时将任务放到Looper所在线程中处理。ui

public Handler(Callback callback, boolean async) {
    ...
    mLooper = Looper.myLooper(); //threadLocal.get获取线程对应的Looper
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;     //经过Looper获取MessageQueue
    mCallback = callback;
    mAsynchronous = async;
}
复制代码

Looper.loop是一个死循环,拿不到须要处理的Message就会阻塞,那在UI线程中为何不会致使ANR?

首先咱们来看形成ANR的缘由:

  1. 当前的事件没有机会获得处理(即主线程正在处理前一个事件,没有及时的完成或者looper被某种缘由阻塞住了)。
  2. 当前的事件正在处理,但没有及时完成。

咱们再来看一下APP的入口ActivityThread的main方法:

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

        Looper.prepareMainLooper();

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

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

        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
复制代码

咱们知道Android 的是由事件驱动的,looper.loop() 不断地接收事件、处理事件,每个点击触摸或者说Activity的生命周期都是运行在 Looper的控制之下,若是它中止了,应用也就中止了。真正的阻塞是由于轮询出message后在处理message消息的时候因为执行了耗时操做致使了ANR,而不是死循环致使的阻塞,没有消息处理的时候消息队列是阻塞在nativePollOnce方法中的,**这个方法使用的是epoll管道机制,Linux底层执行后会释放CPU避免不断死循环形成的CPU浪费。**

Handler 引发的内存泄露缘由以及最佳解决方案

当咱们用Handler发送延时消息时,若是在延时期间用户关闭了 Activity,那么该 Activity 会泄露。这个泄露是由于 Message 会持有 Handler,而又由于 Java 的特性,内部类会持有外部类,使得 Activity 会被 Handler 持有,这样最终就致使 Activity 泄露。 解决该问题的最有效的方法是:将 Handler 定义成静态的内部类(静态内部类不会持有外部类引用,可是静态内部类调用不到外部类的非静态属性和方法,因此咱们须要在内部类中使用弱引用持有Activity,使用弱引用调用到Activity中的方法),并及时移除全部消息。 泄漏时的引用链 Activity->Handler->Message->MessageQueue ,延迟消息会一直在MessageQueue中等待处理,在等待的过程当中有可能会形成内存泄漏。 示例代码以下:

private static class SafeHandler extends Handler {

    private WeakReference<HandlerActivity> ref;

    public SafeHandler(HandlerActivity activity) {
        this.ref = new WeakReference(activity);
    }

    @Override
    public void handleMessage(final Message msg) {
        HandlerActivity activity = ref.get();
        if (activity != null) {
            activity.handleMessage(msg);
        }
    }
}
复制代码

而且再在 Activity.onDestroy() 前移除消息,加一层保障:

@Override
protected void onDestroy() {
  safeHandler.removeCallbacksAndMessages(null);
  super.onDestroy();
}
复制代码

这样双重保障,就能彻底避免内存泄露了。 注意:单纯的在 onDestroy 移除消息并不保险,由于 onDestroy 并不必定执行(如报异常)。

IdleHandler 是什么

从下边MessageQueue的源码可知道,IdleHandler即在MessageQueue应该被阻塞以前去调用(固然前提是你要讲自定义的IdleHandler加入到集合中)IdleHandler接口表示当MessageQueue发现当前没有更多消息能够处理的时候, 则顺便干点别的事情的callback函数(即若是发现idle了, 那就找点别的事干). callback函数有个boolean的返回值, 表示是否keep. 若是返回false, 则它会在调用完毕以后从mIdleHandlers中移除. IdleHandler 能够用来提高提高性能,主要用在咱们但愿可以在当前线程消息队列空闲时作些事情(譬如UI线程在显示完成后,若是线程空闲咱们就能够提早准备其余内容)的状况下,不过最好不要作耗时操做

Message next() {
    final long ptr = mPtr;
    if (ptr == 0) { //当消息循环已经退出,则直接返回
        return null;
    }
    int pendingIdleHandlerCount = -1; // 循环迭代的首次为-1
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        //阻塞操做,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会返回
        //nextPollTimeoutMillis 为-1,一直阻塞,在调用nativeWake(enqueue Message或Looper.quit()退出Looper)时会被唤醒解除阻塞
        //nextPollTimeoutMillis 为0,不阻塞
        //nextPollTimeoutMillis 为>0,阻塞到对应时间后解除,如为10000则阻塞十秒后解除,用于处理延迟消息
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                //当消息Handler为空时,查询MessageQueue中的下一条异步消息msg,则退出循环。
                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 {
                    // 获取一条消息,并返回
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    //设置消息的使用状态,即flags |= FLAG_IN_USE
                    msg.markInUse();
                    return msg;   //成功地获取MessageQueue中的下一条即将要执行的消息
                }
            } else {
                //没有消息,阻塞
                nextPollTimeoutMillis = -1;
            }
            //消息正在退出,返回null
            if (mQuitting) {
                dispose();
                return null;
            }
            //若是当前MessageQueue头结点为空(没有消息要处理了)或者当前系统时间<消息触发时间
            if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
            //看是否加入了idleHandler
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                //没有idle handlers 须要运行,则循环并等待。
                mBlocked = true;
                continue;
            }
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        //只有第一次循环时,会运行idle handlers,执行完成后,重置pendingIdleHandlerCount为0.
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; //去掉handler的引用
            boolean keep = false;
            try {
                //若是queueIdle()返回false则当前idlehandler只能运行一次
                keep = idler.queueIdle();  //idle时执行的方法
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }
            if (!keep) {
                synchronized (this) {
                    //queueIdle返回false,移除idlehandler
                    mIdleHandlers.remove(idler);
                }
            }
        }
        //重置idle handler个数为0,以保证不会再次重复运行
        pendingIdleHandlerCount = 0;
        //当调用一个空闲handler时,一个新message可以被分发,所以无需等待能够直接查询pending message.
        nextPollTimeoutMillis = 0;
    }
}
复制代码

同步屏障

同步屏障是由系统发送,通常用于刷新UI(如16ms刷新一次界面)。当设置了同步屏障以后,next函数将会忽略全部的同步消息,返回异步消息。换句话说就是,设置了同步屏障以后,Handler只会处理异步消息。再换句话说,同步屏障为Handler消息机制增长了一种简单的优先级机制,异步消息的优先级要高于同步消息。

  • 如何判断是否为同步屏障消息? 当Message的target为null时(Message不持有Handler)则当前消息为异步消息,也就是同步屏障。

Android应用框架中为了更快的响应UI刷新事件在ViewRootImpl.scheduleTraversals中使用了同步屏障

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        //设置同步障碍,确保mTraversalRunnable优先被执行
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //内部经过Handler发送了一个异步消息
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}
复制代码
相关文章
相关标签/搜索