代码里部分含有中文注释,也主张认真看一下。当然源码的英文注释也十分有帮助

本文合适对Handler的运用根本了解,想要深化探索其运转机制和源码原理的人群,初学者主张先阅览学习 Android中Handler的根本运用

鉴于内容较多,后续扩展较广,主张依据需求,分批分段阅览,或时常复读,常读常新。愿对加深你的了解有帮助

Handler

先看Handler主要的结构办法和成员变量:Looper、MessageQueue是比较要害的

这儿得到对于一个Looper目标,能够实例化n个Handler与之绑定。刚开端这儿可能还比较懵,先看后面,这儿留个疑问吧:为什么Handler需求与Looper绑定

Handler:Looper=n:1Handler:Looper = n:1
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
// 这个仍是比较眼熟的吧,便是实例化Handler一般会传入的,或许会直接重写本身的handleMessage办法
public interface Callback {
    /**
     * @param msg A {@link android.os.Message Message} object
     * @return True if no further handling is desired
     */
    boolean handleMessage(@NonNull Message msg);
}
// 这两个能够先不看,主张整体知识能够串联起来后再自主回忆一遍
final boolean mAsynchronous;
IMessenger mMessenger;
// 推荐的Handler的获取办法需求传入Looper目标,关于Looper的获取请看对应阶段
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
    mLooper = looper;
    // 其间MessageQueue来自于Looper
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

Handler一般怎样运用

一般运用handler,会实例化一个 Meaasge目标,然后经过 sendMessage 办法发射出去,在重写handleMessage 办法或传入的Callback接口完成中处理回调内容,关于回调怎样运用,这儿就不再阐述了。

// 当然对它自带的分发机制不满意的话,也能够override
public void dispatchMessage(@NonNull Message msg) {
    // 这儿发现Message也有callback,而且是个Runnable,是个互斥逻辑
    // 这儿的callback是调用Handler的post办法时,绑定在Message目标上的
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            // 包含这儿,Callback的完成的回来值假如为true,有点onTouchEvent消费事情的意味,将会阻拦重写的Handler的handleMessage办法
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

在时序图中能够看出大致的调用链,终究会调用 enqueueMessage

一文搞定面试:Handler源码解析
结合代码咱们发现,不管是post、Delayed、AtTime等变体办法,终究实际是将时刻核算为产生时刻uptimeMillis,结合办法名应该是放入了MessageQueue的音讯行列中。 Handler的本身先告一阶段,后面跟着Looper和MessageQueue的解析再继续品吧

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    // 这儿十分要害!!!
    // 这儿对Message目标绑定了target,this也便是Handler本身,这也是为什么后面能收到回调的原因,相当于常用的观察者形式的接口持有
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();
    if (mAsynchronous) {
        // 这儿设置了是否为异步形式,这儿需求结合同步屏障一同食用,能够先不看
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

MessageQueue

首要望文生义,是个保护Message的行列,经过持有头节点掌握行列。内部经过next指针形成链,一起在新节点刺进时,会遵从when的时刻优先

Message mMessages;
public final class Message implements Parcelable {
    // 一般用于区分音讯,即类似于通讯code
    public int what;
    // enqueueMessage的时分传入的uptimeMillis
    public long when;
    // 存储数据的当地
    Bundle data;
    // handler绑定持有
    Handler target;
    // handler.post会把履行的action存在这,终究在dispatch的时分调用
    Runnable callback;
    Message next;
}

MessageQueue的入队

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        // target为空,就会抛反常,所以前面Handler的enqueueMessage中绑定target十分重要
        throw new IllegalArgumentException("Message must have a target.");
    }
    synchronized (this) {
        if (msg.isInUse()) {
            // 这儿能够留个心眼,重视下Message的状况什么时分会改变
            throw new IllegalStateException(msg + " This message is already in use.");
        }
        if (mQuitting) {
            // 调用了quit办法后,mQuitting才为true
            // Looper持有了MessageQueue,能够调用该办法,供给了安全和不安全两种办法
            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;
        }
        // 看,这儿变更了Message的状况,其间的标志位是二进制位运算进行保护的
        msg.markInUse();
        // 之前核算出来的uptimeMillis被存在了Message目标的when特点中
        msg.when = when;
        // p便是一个header节点,Message内部经过next指针形成链表
        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;
            // 当MessageQueue为空且没有idleHandler要处理,mBlocked为true
            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为true需求满意以下几个条件
            // 堵塞标志位mBlocked为true,后面会剖析
            // 头结点target为空,所以想要突破同步屏障,需求防止几个设置target的办法,或许手动置空
            // mAsynchronous需求为true
            // 暂时先了解这些
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                // 循环遍历,prev会到合适刺进的节点
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    // 这儿还有时刻的比较,所以是时刻优先行列
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    // needWake会在这儿被消费掉,取决于p是否是异步音讯
                    // 由于之前唤醒过了,就不需求再唤醒了
                    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;
}

MessageQueue取音讯

这儿还有个比较重要的办法 next() ,由于已知MessageQueue是一个保护Message的行列,那行列需求出队的当地,就在这儿

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) {
        // 当行列遍历时发现mQuitting,即被调用了quit后,ptr即为0
        return null;
    }
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    // 一个死循环,意味着一定会回来一条音讯,否则则会堵塞,直到新音讯到来
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        // 进行堵塞休眠,与enqueueMessage中的nativeWake唤醒对应
        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;
            if (msg != null && msg.target == null) {
                // 这儿就描绘了同步屏障,所以需求记住同步屏障的条件便是target为空
                // 假如需求了同步屏障,就会遍历去找到下一个异步音讯,由于同步的会被堵塞
                // 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;
            }
            // 假如前面找到了合适的Message,就直接return了,这儿就走不进来,所以现在的状况是IDLE的搁置挂起,或许进行了安全退出或退出时,后续没有音讯了
            // 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) {
                // 有IdleHandler就履行,没有那就堵塞标志位mBlocked启用
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }
            if (mPendingIdleHandlers == null) {
                // mPendingIdleHandlers初始化懒加载
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            // mIdleHandlers copy一份到mPendingIdleHandlers,由于mPendingIdleHandlers中取出即会置空
            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 {
                // 调用IdleHandler接口的queueIdle,并依据其回来值决议是否remove掉
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }
            if (!keep) {
                synchronized (this) {
                    // mPendingIdleHandlers仅仅一个暂时行列,而mIdleHandlers才用来持久维持,需求keep的则不会被remove,下次还会被copy过来
                    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;
    }
}

Looper

Looper是怎样来的?

Looper在结构中会绑定当时线程,而且会实例化一个MessageQueue 所以MessageQueue和Looper是1:1联络

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

结构是私有的,该怎样调用呢?

只有 prepare 办法供给了结构的调用,也便是说想要获取Looper,先得调用Looper.prepare 实例化后存入ThreadLocal不能屡次调用,否则会抛反常

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

这儿附了ThreadLocal的set办法,帮助咱们了解。 ThreadLocal能够实例化多个,但终究都会归到 所属Thread的ThreadLocalMap目标,即每个线程都有的一个本地备份,且相互之间不搅扰

所以得到,对应联络和层级持有联络如先后次序所示

Thread:Looper:MessageQueue=1:1:1Thread:Looper:MessageQueue = 1:1:1
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

为什么Handler实例化的时分,不传Looper也能够?

尽管这个办法现已被弃用了,但还能够正常运用

发现代码段中有空判别和反常抛出,那为什么在主线程这么实例化Handler没有问题呢,来接着看看 myLooper 办法

public Handler(@Nullable Callback callback, boolean async) {
    // ……省掉一部分
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        // 假如Looper.myLooper()为空的话,那就会抛反常
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    // 这边就都一样了
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

前面介绍过了ThreadLocal,已然主线程不会溃散,也就意味着主线程有当地帮咱们调用了prepare 办法进行了Looper的实例化

咱们能够测验new Thread.start后在里边经过上面的这种结构实例化一下Handler看看会不会崩,然后再测验自己防止它的溃散

public static @Nullable Looper myLooper() {
    // 由于prepare()不允许屡次调用,所以在实例化前能够经过该办法进行查看,为空才实例化
    return sThreadLocal.get();
}

有心人应该发现了,前面的代码段中有一个办法 prepareMainLooper ,便是猜测和主线程有关,所以全局查找一下,发现在 ActivityThread类中有调用

// 帮咱们处理了一下,只留了需求重点重视的部分
// main办法是一个Activity运用运转的主进口
public static void main(String[] args) {
    // ……省掉一部分办法
    // 1.实例化Looper,所以是源码中帮咱们调用了,为什么呢,由于它需求用呀
    Looper.prepareMainLooper();
    // ……省掉一部分办法
    if (false) {
        // 2.这儿日志标签和 一种卡顿监控有相关,能够留个心眼
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }
    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    // 3.新办法loop!!!
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

loop()是干什么的?

主要就一个查看和一个死循环调用 loopOnce

// 又是一个静态办法呢
public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        // 所以在调用loop()前,一定需求确保prepare过了
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    // ……省掉一部分代码
    for (;;) {
        // 又是一个死循环,这儿一般会引申一个问题,主线程死循环不会卡死吗
        // ActivityThread.main终究就这一个进口了,假如这儿结束了,运用也就结束了
        // 关于休眠机制,后面再讲
        if (!loopOnce(me, ident, thresholdOverride)) {
            return;
        }
    }
}

loopOnce里主要就两步,取出音讯并分发

到这,根本的handler内容就结束了

private static boolean loopOnce(final Looper me,
        final long ident, final int thresholdOverride) {
    // 1.取音讯
    Message msg = me.mQueue.next(); // might block
    if (msg == null) {
        // No message indicates that the message queue is quitting.
        return false;
    }
    // ……省掉一部分代码
    try {
        // 2.分发音讯,这儿的target调用的分发,也便是之前最早enqueueMessage里绑定的handler
        msg.target.dispatchMessage(msg);
        if (observer != null) {
            observer.messageDispatched(token, msg);
        }
        dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
    }
    // ……省掉一部分代码
    // 3.recycleUnchecked将分发完的Message进行收回复用
    // sPool的最大长度为50,空音讯被开释后会被放在表头,obtain复用时会直接取用头节点
    msg.recycleUnchecked();
    return true;
}

进阶,看看以下问题

以下问题均来历于此博客,经一定的收拾调整,咱们也能够直接看这个博客。也有一部分问题以为您认真地看过了上面的内容,现已融汇贯通了,所以就没有写答案

27道 Handler 经典面试题,请留意查收

1. 子线程拜访UI的 溃散原因 和 解决办法?为什么主张子线程不拜访(更新)UI?

更精确地说,“创立UI的线程才干更新UI”,ViewRootImpl的checkThread进行了线程查看,也便是常见的子线程拜访UI引发的溃散,当然也是有控件不在主线程创立的,感兴趣的话能够自行了解。已然知道了原因,那就能够依据场景去合理防止,比如经过handler的通讯机制\协程等手段切换到主线程进行UI操作

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}

Android中UI控件是线程不安全的,加锁会下降UI拜访的效率,造成卡顿,一起也会使UI更新整体复杂化,所以归纳考虑,有了 单线程模型 机制

2. MessageQueue是干嘛呢?用的什么数据结构来存储数据?延迟音讯是怎样完成的?MessageQueue的音讯怎样被取出来的?MessageQueue没有音讯时分会怎样?堵塞之后怎样唤醒呢?

不供给答案,enqueueMessage(同步音讯、异步音讯入队)时进行唤醒nativeWake,当next()行列中没有音讯或下一个音讯还未到时刻履行时休眠nativePollOnce

3. 说说pipe/epoll机制?

epoll机制是一种IO多路复用的机制,详细逻辑便是一个进程能够监督多个描绘符,当某个描绘符就绪(一般是读就绪或许写就绪),能够告诉程序进行相应的读写操作,这个读写操作是堵塞的。在Android中,会创立一个Linux管道(Pipe)来处理堵塞和唤醒。 浅谈Android之Linux pipe/epoll 尽管看完也还有点懵hhh,就先这样吧

4. 同步屏障和异步音讯是怎样完成的?同步屏障和异步音讯有详细的运用场景吗?

差异于异步音讯的是同步音讯,也便是一般音讯,而异步音讯经过Message目标调用setAsynchronous。假如handler设置为异步,一切音讯都会设置setAsynchronous。正常情况下,异步音讯和一般音讯一样,会依据when时刻戳排队按序分发。当遇到同步屏障时,会唤醒线程,而且跨过期间的同步音讯

同步屏障是一种机制,特点是target为空,能够经过MessageQueue的postSyncBarrier(需求反射才干调用)办法(成功会有一个回来值code,在remove时需求用到。一起它不会真正意义上入队不走enqueueMessage,而是被直接放在了行列的恰当方位,所以它不会引发线程唤醒)发送。同步屏障不归于一种音讯,归于一种堵塞机制,意味着要优先处理异步音讯

ViewRootImpl.scheduleTraversals办法中,运用同步屏障和异步音讯的组合运用,用于垂直同步信号脉冲的监听,而且ASYNC信号到来之后,保证UI制作优先履行(防止期间有太多音讯,导致制作延后而导致卡顿),再移除同步屏障

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        // 发送同步屏障
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        // 这儿能够跟进去看一下,发送了一个异步音讯
        // 这儿设置了一个callback:mTraversalRunnable
        // 里边调用了doTraversal移除了同步屏障,并开端performTraversals
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}
// postCallback -> postCallbackDelayed -> postCallbackDelayedInternal
private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) {
        // ……省掉一部分代码,这个音讯终究在Choreographer中处理
        // FrameDisplayEventReceiver
        Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
        msg.arg1 = callbackType;
        // 设置异步音讯
        msg.setAsynchronous(true);
        mHandler.sendMessageAtTime(msg, dueTime);
}

当然有优点,也有代价。对于VSYNC信号的机制,同步音讯最多可能被延迟一帧,一起会造成同步音讯的堆积,集中处理时会有压力。所以同步音讯的履行时刻是不一定精确的!!!

那运用异步音讯需求留意点什么呢:

1.轻量级操作,否则无论怎样都会使得主线程压力变大

2.不需求和制作次序同步的,那就能够运用,否则仍是运用同步handler

Handler 音讯行列中的同步屏障——Message

5. Message是怎样找到它所属的Handler然后进行分发的?Message音讯被分发之后会怎样处理?音讯怎样复用的?

不供给答案

6. Looper是干嘛呢?怎样获取当时线程的Looper?能够屡次创立Looper吗?为什么主线程不需求独自创立Looper?(ActivityThread中做了哪些关于Handler的工作)

不供给答案

7. ThreadLocal运转机制?这种机制规划的优点?还有哪些当地运用到了ThreadLocal机制?为什么不直接用Map存储线程和目标呢?

一个Thread持有一个ThreadLocalMap,其间用Entry数组保护key(ThreadLocal目标)-value(存的泛型),类似于hashMap,会经过ThreadLocal.threadLocalHashCode和位运算得到index

static class ThreadLocalMap {
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
    private Entry[] table;
}

这种机制保证了线程间的数据阻隔,而用Map存储就会造成线程间共享变量的混乱污染。所以适用于线程间不共享变量的线程目标保护。需求运用时需求留意内存走漏,及时进行remove,由于key是弱引证

Choreographer中也运用了static ThreadLocal完成了主线程单例,由于key是以ThreadLocal目标的,所以static能够使得一个线程内一切操作共享

8. Looper中的quitAllowed字段是啥?有什么用?

望文生义,是否允许退出的标记(Looper.prepare->结构中传入),主线程在创立时传入的是false(即不允许退出),终究给到MessageQueue,会在调用其quit时起作用

// 这儿还有个safe标记
void quit(boolean safe) {
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }
    synchronized (this) {
        if (mQuitting) {
            return;
        }
        // 新的音讯就不会再进来了
        mQuitting = true;
        if (safe) {
            // 安全退出,now时钟之前的音讯需求处理,但之后的音讯都recycler收回了
            // 这也是为什么在MessageQueue.next()中,对mQuitting放这么后面
            removeAllFutureMessagesLocked();
        } else {
            // 不安全,则直接悉数清空收回
            removeAllMessagesLocked();
        }
        // We can assume mPtr != 0 because mQuitting was previously false.
        nativeWake(mPtr);
    }
}

那什么时分才会调用quit()?

APP需求退出,则主线程调用

子线程处理结束,则调用开释资源

9. Looper.loop办法是死循环,为什么不会卡死(ANR)?

当没有音讯的时分,会堵塞在loop的queue.next()中的nativePollOnce()办法里,此时主线程会开释CPU资源进入休眠状况,直到下个音讯到达或许有业务产生。所以死循环也不会特别消耗CPU资源。

而且主线程便是需求一向履行,处理各种音讯(各种Message,比如Activity的生命周期等)。而导致ANR的是音讯的处理耗时

10. Handler 的 post(Runnable) 与 sendMessage 有什么差异?Handler.Callback.handleMessage 和 Handler.handleMessage 有什么不一样?为什么这么规划?

不供给答案

11. Handler、Looper、MessageQueue、线程是一一对应联络吗?

不供给答案

12. IdleHandler是啥?有什么运用场景?

回忆一下next中,当MessageQueue中没有音讯,在堵塞之前,还会去处理IdleHandler和mBlocked堵塞标志位。能够经过MessageQueue的addIdleHandler进行增加

Looper.myLooper()?.queue?.addIdleHandler {
    // do something
    // 回来true代表keep,false即处理完就remove
    false 
}

性质:在不影响其他任务,在MessageQueue空闲状况下履行

运用:Android Framework层的GC场景就运用了这个机制,只有当cpu空闲的时分才会去GC

final class GcIdler implements MessageQueue.IdleHandler {
    @Override
    public final boolean queueIdle() {
        doGcIfNeeded();
        purgePendingResources();
        return false;
    }
}

切记~!如像View.onDraw中无限制调用invalidate,不断增加同步屏障,在等到异步音讯之前会一向堵塞在next()中,而这儿的下一个异步任务又是制作相关的,履行onDraw而无限循环,从而无法履行IdleHandler

13. HandlerThread是啥?有什么运用场景?

HandlerThread便是为了便于在子线程中运用Handler而封装调用了Looper.parpare()&loop()的模板Thread,其间的notify\wait挺有趣的,能够看一下

@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        // 与getLooper中的同步锁对应,进行唤醒
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}
/**
 * This method returns the Looper associated with this thread. If this thread not been started
 * or for any reason isAlive() returns false, this method will return null. If this thread
 * has been started, this method will block until the looper has been initialized.  
 * @return The looper.
 */
public Looper getLooper() {
    if (!isAlive()) {
        // 这时说明Thread还没start,当然也没创立Looper
        return null;
    }
    // If the thread has been started, wait until the looper has been created.
    synchronized (this) {
        while (isAlive() && mLooper == null) {
            try {
                // 这儿结合同步锁,等候Looper创立结束
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}

14. IntentService是啥?有什么运用场景?

是一个内部保护了HandlerThread的Service,Service发动后,会发送Message回调onHandleIntent(),履行结束后经过stopSelf进行关闭Service,一起onDestory时也及时对Looper进行quit,进行了合理收回。主张结合16题的内存走漏来看,这是防止该问题的杰出实践

public abstract class IntentService extends Service {
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }
    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }
    public void onDestroy() {
        mServiceLooper.quit();
    }

15. BlockCanary运用过吗?说说原理

BlockCanary是一个用来检测运用卡顿耗时的三方库。其原理主要对Looper设置了MessageLogging,完成对主线程音讯耗时的监听

public static void loop() {
    for (;;) {
        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        msg.target.dispatchMessage(msg);
        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }
    }
}
// 注入自己的Printer
Looper.getMainLooper().setMessageLogging(new Printer() {
    private static final String START = ">>>>> Dispatching";
    private static final String END = "<<<<< Finished";
    @Override
    public void println(String x) {
        if (x.startsWith(START)) {
            // 对指定格局音讯进行监听,而且依据自己的卡顿检测需求dump堆栈
            startMonitor();
        }
        if (x.startsWith(END)) {
            removeMonitor();
        }
    }
});

16. 说说Hanlder内存走漏问题。

“内部类持有了外部类引证”,先整理一下Handler的调用链,即ThreadLocal本身作为弱引证,即将开释时,其value:Looper直接持有Activity,因其可达而无法收回Activity,但key:ThreadLocal现已被收回了,后续value一向没有被主动收回,这本身也是ThreadLocal的内存走漏问题

Thread -> ThreadLocal -> Looper -> MessageQueue -> Message(target持有) -> Handler -> 持有或操作Activity

即当Activity退出时,Handler仍可达(比如在处理耗时操作,或MessageQueue还在排队中持有Handler)

那应该怎样处理呢?两步走

1.Handler对外部类,如Activity进行弱引证 2.Activity.onDestory时,堵截Message与Handler之间的联络,即强迫履行Message的recycle(),target置为null

openclassWeakReferenceHandler<T>(looper:Looper?,referencedObject:T):Handler(looper!!){
privatevalmReference:WeakReference<T>=WeakReference(referencedObject)  
protectedvalreferencedObject:T?  
protectedget()=mReference.get()  
}
// Activity 毁掉的时分,假如子线程任务尚未结束,及时中止 Thread:  
 overridefunonDestroy(){  
     ...
     // 协程的话,会相关lifecycleScope进行生命周期监听并及时cancel
     thread.interrupt()  
 }  
//假如子线程中创立了 Looper 并成为了 Looper 线程的话,须手动 quit。比如HandlerThread:  
 overridefunonDestroy(){  
     ...  
     // 假如用协程进行线程切换的话,也不存在这个问题,由于直接被cancel掉了
     handlerThread.quitSafely()  
 }  
//主线程的 Looper 无法手动 quit(由于设置了quitAllow=false),所以还需手动清空主线程中 Handler 未处理的 Message:  
 overridefunonDestroy(){  
     ...  
     mainHandler.removeCallbacksAndMessages(null)  
 }  
 ※1:Message 在履行`recycle()`后会铲除其与和 Main Handler 的引证联络  
 ※2:Looper 子线程调用 quit 时会清空 Message,所以无需针对子线程的 Handler 再作 Message 的清空处理了

有任何 主张\纠正 欢迎下方谈论联络作者