Handler 的理解

几个关于Handler 的思考问题

Handler 消息管理机制,如何管理事务?
Handler在线程间如何通信?
Handler 内存共享方案

架构简图

->「主线程,消费者」queue.next
[Message] <--------[Looper]     ===>[dispatchMessage]===>[handleMessage]
[Message]    
[Message]               
[Message]
[Message] 
.
.
.
[Message] <-----queue.enqueueMesage-----[Handler]
->「子线程,生产者」

我们使用时候的过程
子线程(bean)-> 主线程(显示)

涉及到的知识点

ActiviyThread->AMS 流程
ActivitThead 里面的main()方法
我们的应用启动过程
Launcher(app):zygote->jvm->ActivityThread.main()==>[Looper.prepareMainLooper() ------looper.loop]

设计思路,handler 的message消息队列为什么不阻塞?
这是因为整个系统都再用handler 的message机制。如果阻塞会产生手机卡死的情况。
设计模式,是一种生产折消费者常用模式,生产--->缓存池----->消费
生产者,通过enqueueMessage ----->[MessageQueue 仓库]-----> next() 取出消息,消费者。
异步消息,同步消息
看完这个链接,或者看完本文

消息屏障/handlerThread IntentService
看完这个链接,或者看完本文

参考掘金: https://juejin.im/post/6844903910113705998

MessageQueue特殊情况
两个方面的阻塞:

  1. 消息没到执行时刻,会根据when 阻塞,计算nextpollTimeout---nativePollOnce
    自动唤醒

2.消息队列为空的时候 nativePollOnce---ptr -nativeInt()
无限等待状态。
当其他消息进来就会唤醒

源码分析

开始

使用的入口
Hndler=>
「无论调用那个postAtTime 最后会执行」
「无论调用那个sendMessage最后会执行」
=>sendMessageDelayed(Message msg, long delayMillis)
=> sendMessageAtTime(Message msg, long uptimeMillis)
「uptimeMills====SystemClock.uptimeMillis() + delayMillis 系统时间+我们传入的延时时间 ,在那个时间点执行」
=>enqueueMessage(queue, msg, uptimeMillis)

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

传送带功能

MessageQueue跟随Looper 顺带创建的

MessageQueue数据结构
单链表实现的优先级队列(根据时间条件优先级判断)
根据时间优先级,是一个插入排序算法,优先级队列是取消息是从队列头开始,

MessageQueue 持有 (Message mMessages;)
链表 Message->next->Message->next

Message => 持有(Handler target;)注意这个是Message 持有target相当于Handler自身this,由于消息发送可能延时,我们Activity声明周期结束,但是内部类持有外部引用,造成内存泄露的原因之一

MessageQueue 的 mQuitting默认false调用quit ---true
quit清空消息,调用本地nativeWake唤醒
下面的Looper

void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

MessageQueue 的 synchronized (this)
内置锁保证安全
一个线程只有一个MessageQueue,而且MessageQueue还有内置对象锁所以保证唯一

 boolean enqueueMessage(Message msg, long when) {

  ->「注意这里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) {
  ->「注意这里mQuitting」
            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;
->「如果消息队列有消息,判断节点p,根据我要加入消息when比p时间早,轮询判断进行插入」
            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) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

轮询取消息

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;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
->「nativePollOnce 承接上文唤醒队列」
            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;
->「 如果target==null,那么它就是屏障,需要循环遍历,一直往后找到第一个异步的消息
」
                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.
                        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;
                }

                // 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();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                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();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    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.
            nextPollTimeoutMillis = 0;
        }
    }

我们有很多native方法。系统实现了很多JNI层
nativePollOnce(睡眠)/nativeWake 唤醒操作
底层调用epoll方法,使线程阻塞

private native static long nativeInit();
    private native static void nativeDestroy(long ptr);
    @UnsupportedAppUsage
    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);

动力

Looper 相关

-> Looper 的初始化
构造函数是私有的,不能随便创建

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

持有一个引用final MessageQueue mQueue;
我们的MessageQueue 就是在这里顺带创建的

在prepare里面进行的初始化

private static void prepare(boolean quitAllowed) {
->「注意承接上面,保证唯一行threadLocal get 检查」
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

所有的当前线程的Looper myLooper方法,唯一性

/**
     * 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

初始化中
static final ThreadLocal sThreadLocal = new ThreadLocal();

上下文存储变量,内部有一个ThreadLocalMap k 就是threadlocal 本身,而他是final的 ,每次set先get(k)如果存在直接返回

是一个常量保证了唯一性

ThreadLocal 维护类自己的Map

  static class ThreadLocalMap {
  static class Entry extends WeakReference> {
   Object value;

            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            } 
}
...
  }

一个线程----》只有一个Looper 不能改
一个线程----》ThreadLocalMap---》<唯一的ThreadLocal,value>

内部维护原子性操作
private static AtomicInteger nextHashCode =
new AtomicInteger();

prepare 的时候先get 判断是否存在

Looper loop()

执行流程
looper.loop()->messageQueue.next()->handler.dispatchMessage->handler.handlerMessage()

承接Message
「msg.target 相当于====handler===dispatchMessage 回调」

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;

        // 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) {
                // No message indicates that the message queue is quitting.
                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);
            }
            // Make sure the observer won't change while processing a transaction.
            final Observer observer = sObserver;

            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;
            Object token = null;
            if (observer != null) {
                token = observer.messageDispatchStarting();
            }
            long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
            try {
->注意留意「msg.target」
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    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);
            }

            msg.recycleUnchecked();
        }
    }

结束

Handler 的Callback
Handler->handlerMessage()

共享内存

整个过程是Message在动的过程
Message产生,new Message() 或者 obtain过来,所以就体现子线程到主线程的内存共享

内存不分线程,所以子线程能用,主线程也能用。

也是为什么不能不断new Message的原因

退出和空消息

如果子线程调用 消息为空如何处理
需要主动调用quite,Looper 的loop 是个无限循环,msg == null

quit-》唤醒-》messagequeue = null -> 退出Looper
quit函数---》nativeWake()

Handler 内存泄露的原因和解决

  1. Activity 使用错误,使用了匿名内部类。默认会持有 this,handler 持有外部类Activity。-----------解决改成static 静态内部类

Handler handler = new Handler(){
......
}

  1. 其他的内部类持有为什么没有这种问题,recyclerview 的Adapter 的viewholder?声明周期的原因

sendMessageAtTime--->enqueueMessage{
msg.target = this;=====handler
}
整个message传递过程,需要延迟处理,message持有handler,handler持有activity。GC无法回收,可达性算法不能回收
------------------解决 在 onDestroy的时候 主动调用removeCallbacksAndMessages 释放

HandlerThread是什么?为什么它会存在?

HandlerThread是Thread的子类,严格意义上来说就是一个线程,只是它在自己的线程里面帮我们创建了Looper HandlerThread 存在的意义如下:

  1. 方便使用:a. 方便初始化,b,方便获取线程looper 2)保证了线程安全
    我们一般在Thread里面 线程Looper进行初始化的代码里面,必须要对Looper.prepare(),同时要调用Loop。 loop();
@Override
public void run() { Looper.prepare(); Looper.loop();
}

而我们要使用子线程中的Looper的方式是怎样的呢?看下面的代码

Thread thread = new Thread(new Runnable() {
    Looper looper;
@Override
public void run() {
// Log.d(TAG, "click2: " + Thread.currentThread().getName());
Looper.prepare();
looper =Looper.myLooper(); Looper.loop();
}
    public Looper getLooper() {
        return looper;
} });
thread.start();
Handler handler = new Handler(thread.getLooper());

上面这段代码有没有问题呢? 肯定有
1)在初始化子线程的handler的时候,我们无法将子线程的looper传值给Handler,解决办法有如下办法:
a. 可以将Handler的初始化放到 Thread里面进行
b. 可以创建一个独立的类继承Thread,然后,通过类的对象获取。
这两种办法都可以,但是,这个工作 HandlerThread帮我们完成了
2)依据多线程的工作原理,我们在上面的代码中,调用 thread.getLooper()的时候,此时的looper可能还没有初 始化,此时是不是可能会挂掉呢?
以上问题
HandlerThread 已经帮我们完美的解决了,这就是 handlerThread存在的必要性了。

我们再看 HandlerThread源码

public void run() {
mTid = Process.myTid(); Looper.prepare(); synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); //此时唤醒其他等待的锁,但是 }
Process.setThreadPriority(mPriority); onLooperPrepared();
Looper.loop();
mTid = -1;
}

它的优点就在于它的多线程操作,可以帮我们保证使用Thread的handler时一定是安全的。

IntentService 默认创建HandlerThread 参见 https://www.jianshu.com/p/ad5613d3955e

应用场景1:

IntentService 应用:本身继承service: 处理后台耗时任务
处理完》 IntentService 自动停止:内存释放

应用需求:一项任务分成几个子任务,子任务按顺序执行,子任务全部执行完成后,这项任务才算成功
这个需求可以用多个线程来处理,一个线程处理完 -> 下一个线程 -> 下一个线程

IntentService就可以帮助我们完成这个工作,而且,能够很好的管理线程,保证只有一个线程处理工作,而且是一个一个的完成任务,有条不紊的进行
同一个线程-》顺序执行1 2 3 4 : 对线程的控制么
原因:
IntentService 每个HandlerThread 维护一个Looper 保证looper绑定
每个任务是一个消息,维护了一个队列messageQueue
先来后到 保证线程消息一个一个执行。

类似的场景2:缓存操作参见
Messagequeue 队列处理机制在fragment生命周期管理中的应用,glide

https://www.jianshu.com/p/317b2d6bde1b

题外话:我们在Glide里面handler的场景

Glide.with(context).from(url).into(iamgeView)
我们通常context 缩小范围,如果默认用子线程他会默认给你转成applicationContext容易造成泄露
context:? fragment.getAppalicationContext

 RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
    
    pendingRequestManagerFragments = new HashMap();
    //尝试根据id去找到此前创建的RequestManagerFragment
    RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
        //如果没有找到,那么从临时存储中寻找
        current = pendingRequestManagerFragments.get(fm);
        if (current == null) {
            //如果仍然没有找到,那么新建一个RequestManagerFragment,并添加到临时存储中。
            //然后开启事务绑定fragment并使用handler发送消息来将临时存储的fragment移除。
            current = new RequestManagerFragment();
            pendingRequestManagerFragments.put(fm, current);
            fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
            handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
        }
    
    }

场景3:Fragment的声明周期管理,也用到了MessageQueue和handler相关东西。

android View中的Looper Handler message

Choreographer 屏幕的点击 「翻译过来是舞者」
同步屏障,保证不掉帧 vsync
我们现在手机最低60hz ,也就是刷新率1000ms/60帧率=16帧/ms

消息机制之同步屏障

线程的消息都是放到同一个MessageQueue里面,取消息的时候是互斥取消息,而 且 ,而添加消息是按照消息的执行的先后顺序进行的排序,那么问题来了,同一个时间范围内的消 息,如果它是需要立刻执行的,那我们怎么办,按照常规的办法,我们需要等到队列轮询到我自己的时候才能执行 哦,那岂不是黄花菜都凉了。所以,我们需要给紧急需要执行的消息一个绿色通道,这个绿色通道就是同步屏障的概 念
同步屏障是什么?
屏障的意思即为阻碍,顾名思义,同步屏障就是阻碍同步消息,只让异步消息通过。如何开启同步屏障呢?如下而 已:

 MessageQueue#postSyncBarrier()

源代码是这样

 /**
     * Posts a synchronization barrier to the Looper's message queue.
     *
     * Message processing occurs as usual until the message queue encounters the
     * synchronization barrier that has been posted.  When the barrier is encountered,
     * later synchronous messages in the queue are stalled (prevented from being executed)
     * until the barrier is released by calling {@link #removeSyncBarrier} and specifying
     * the token that identifies the synchronization barrier.
     *
     * This method is used to immediately postpone execution of all subsequently posted
     * synchronous messages until a condition is met that releases the barrier.
     * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier
     * and continue to be processed as usual.
     *
     * This call must be always matched by a call to {@link #removeSyncBarrier} with
     * the same token to ensure that the message queue resumes normal operation.
     * Otherwise the application will probably hang!
     *
     * @return A token that uniquely identifies the barrier.  This token must be
     * passed to {@link #removeSyncBarrier} to release the barrier.
     *
     * @hide
     */
    @TestApi
    public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        // 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;
        }
    }

可以看到,Message 对象初始化的时候并没有给 target 赋值,因此, target == null 的 来源就找到了。上面消 息的插入也做了相应的注释。这样,一条target == null 的消息就进入了消息队列。
那么,开启同步屏障后,所谓的异步消息又是如何被处理的呢? 如果对消息机制有所了解的话,应该知道消息的最终处理是在消息轮询器 Looper#loop() 中,而 loop() 循环中会
调用 MessageQueue#next() 从消息队列中进行取消息。

->上面的「传送带」Message next() 查看源码

从上面可以看出,当消息队列开启同步屏障的时候(即标识为msg.target == null),消息机制在处理消息的时 候,优先处理异步消息。这样,同步屏障就起到了一种过滤和优先级的作用。

同步屏障.png

同步消息的应用场景

似乎在日常的应用开发中,很少会用到同步屏障。那么,同步屏障在系统源码中有哪些使用场景呢?Android 系统中
的 UI 更新相关的消息即为异步消息,需要优先处理。
比如,在 View 更新时,draw、requestLayout、invalidate 等很多地方都调用了
ViewRootImpl#scheduleTraversals() ,如下:

//ViewRootImpl.java

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
-》【开启同步屏障】
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
-》【发送异步消息】
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

postCallback() 最终走到了

Choreographer-》postCallbackDelayedInternal() :



private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
        if (DEBUG_FRAMES) {
            Log.d(TAG, "PostCallback: type=" + callbackType
                    + ", action=" + action + ", token=" + token
                    + ", delayMillis=" + delayMillis);
        }

        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

            if (dueTime <= now) {
                scheduleFrameLocked(now);
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
-》【异步消息】
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

这里就开启了同步屏障,并发送异步消息,由于 UI 更新相关的消息是优先级最高的,这样系统就会优先处理这些异 步消息。
最后,当要移除同步屏障的时候需要调用 ViewRootImpl#unscheduleTraversals() 。

void unscheduleTraversals() {
    if (mTraversalScheduled) {
} }

小结
同步屏障的设置可以方便地处理那些优先级较高的异步消息。当我们调用
Handler.getLooper().getQueue().postSyncBarrier() 并设置消息的 setAsynchronous(true) 时,target 即 为 null ,也就开启了同步屏障。当在消息轮询器 Looper 在 loop() 中循环处理消息时,如若开启了同步屏障,会优 先处理其中的异步消息,而阻碍同步消息。

享元设计模式(内存复用)

Message
obtain() 用的一个对象池

    public static final Object sPoolSync = new Object();

message 用的是对象池
当结束后,会回收

点击事件会转换成message发送出去

Looper 的 loop死循环 跟ANR 没有关系,只是ANR发生 5s handler会发出提醒
ActivityThread的 main 方法的主要作用就是做消息循环,一旦退出消息循环,主线程运行完毕,那么你的应用也就退出了。Android是事件驱动的,在Looper.loop()中不断接收事件、处理事件,而Activity的生命周期都依靠于主线程的 Loop.loop() 来调度,所以可想而知它的存活周期和 Activity 也是一致的。当没有事件需要处理时,主线程就会阻塞;当子线程往消息队列发送消息,并且往管道文件写数据时,主线程就被唤醒。真正会卡死主线程的操作是在执行回调方法 onCreate/onStart/onResume 等操作的时间过长,导致掉帧,甚至发生ANR,looper.loop() 本身不会导致应用卡死。

主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。

你可能感兴趣的:(Handler 的理解)