Handler(一)源码解析

前言

Android Handler基本是面试必问的知识点,也是了解Android源码的第一步,那么Handler到底是什么呢?接下来我们一探究竟。

Handler基础

Handler主要涉及4个类,分别是Handler、Looper、Message、MessageQueue。

HandlerLooperMessage

MessageQueue

Handler发送消息-处理消息流程

那么它们是如何相互作用,来实现Handler的功能的呢?

Handler发送消息,处理消息流程

1、通过调用sendMessage、post等方法,会最终调用到Handler的enqueueMessage方法,此方法会向MessageQueue的mMessages中新增一个节点。
发送消息

2、当线程中Looper.loop调用时,会启动一个死循环,轮询获取Message信息。

public static void loop() {
    ········
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;
    for (;;) {
        Message msg = queue.next(); // might block
        ········
        msg.target.dispatchMessage(msg);
    }
    ········
}

3、当获取到Message消息后,通过msg.target.dispatchMessage(msg)方法,回调给Handler。

初始化流程

初始化流程

1、Handler初始化的时候,需要传入Looper,或者通过Looper.myLooper()获取当前线程的Looper;并得到looper.mQueue对象。

public Handler(@NonNull Looper looper) {
    this(looper, null, false);
}

public Handler(@Nullable Callback callback, boolean async) {
    ........
    mLooper = Looper.myLooper();
    mQueue = mLooper.mQueue;
    ........
}

2、Looper初始化的时候,会创建MessageQueue对象。

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

3、在Handler发送消息时,会将handler赋值给Message.target

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

Handler为什么会内存泄露

由此,我们可以看到如下的持有链,当长生命周期的对象(Thread)持有短生命周期的对象(真正处理回调的类,如Activity),内存泄漏便发生了。

Handler的持有链

Handler常见问题

1、Message可以如何创建,哪种效果更好?

message可以通过new Message()、Message.obtain()、handler.obtainMessage()创建,用后两种方式创建效果比较好。

Message回收池

在Message中,通过单向链表构建了Message的缓存池,防止对象频繁创建、销毁造成内存抖动。

public static final Object sPoolSync = new Object();// 锁
private static Message sPool;// 缓存池
private static int sPoolSize = 0;// 缓存的个数
private static final int MAX_POOL_SIZE = 50;// 最大缓存个数

// 通过缓存池获取Message对象,若缓存池为null,则创建新的Message
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

// 回收Message对象
void recycleUnchecked() {
    // Mark the message as in use while it remains in the recy
    // Clear out all other details.
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = UID_NONE;
    workSourceUid = UID_NONE;
    when = 0;
    target = null;
    callback = null;
    data = null;
    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

2、多个Handler往MessageQueue中添加数据(发消息时各个Handler处于不同线程),内部如何保证安全的?

我们翻看MessageQueue的源码,即可发现,在向MessageQueue的mMessages添加数据时,进行了加锁处理。

boolean enqueueMessage(Message msg, long when) {
    ········
    synchronized (this) {
    ········
    }
    return true;
}

3、Looper.loop为什么不会导致主线程卡死

app启动流程

当App启动时,Zygote进程会fork新的进程,然后通过ActivityThread的main方法进入应用程序。在main方法中,会启动Looper.loop,而在loop之前,会通过thread.attach绑定ActivityManagerService。当系统有事件响应时,便会通知ActivityThread的mH进行处理。

public static void main(String[] args) {
    ········
    Looper.prepareMainLooper();
    ········
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);
    ········
    Looper.loop();
}

// 用于处理AMS响应的事件
class H extends Handler {
    public static final int BIND_APPLICATION        = 110;
    public static final int EXIT_APPLICATION        = 111;
    public static final int RECEIVER                = 113;
    public static final int CREATE_SERVICE          = 114;
    public static final int SERVICE_ARGS            = 115;
    public static final int STOP_SERVICE            = 116;
    ········
}

4、一个线程有几个Looper?可以有几个Handler?

一个线程只能有一个Looper,Looper.loop的时候,会形成死循环,此时即使有多个Looper也没有意义。那是怎么保证Looper的关系呢?通过ThreadLocal保证Looper和Thread的关系。

而一个线程可以有很多个Handler,只要在初始化的时候得到线程的Looper即可。

// sThreadLocal.get() will return null unless you've called prepare().
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

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

/**
 * 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();
}
5、Handler.postDelay后,消息队列会有什么变化?

根据Handler消息发送流程,我们知道,无论通过何种方法调用,最终都会调用到enqueueMessage方法。
观察一下源码,参数中有一个uptimeMillis参数;它是通过SystemClock.uptimeMillis() + delayMillis计算出来的。

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    // 调用MessageQueue的enqueueMessage
    return queue.enqueueMessage(msg, uptimeMillis);
}

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    // 计算更新时间
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

当把消息插入队列时:
1、如果消息队列为null 或 更新时间是0 或 当前消息更新时间<队头元素的更新时间,则将消息添加至队头,并设置唤醒标识。
2、否则遍历消息队列中的元素,按时间顺序将消息插入队列中,这种情况不需要唤醒。
3、如果最终唤醒标识为true,会进行唤醒逻辑处理。

boolean enqueueMessage(Message msg, long when) {
    ········
    synchronized (this) {
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        // 如果消息队列为null 或 更新时间是0 或 当前消息更新时间<队头元素的更新时间
        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 
            // up the event queue unless there is a barrier at the head of the que
            // 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.
        // 唤醒,Message msg = queue.next()停止阻塞
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值