android进阶篇17、深入理解Handler源码解析

前言:

我们先宏观了解一下Handler通过sendMessage发送消息和处理消息的过程,如下所示,后面会详细分析:

handler.sendMessage -> messageQueue.enqueueMessage -> (looper.loop()) -> messageQueue.next() -> msg.target.dispatchMessage(msg) -> handler.handleMessage()

一、Handler源码解析

1、Handler构造方法

Handler的构造方法根据我们是否传入looper参数分为两类;

1)、当我们调用public Handler(Looper looper)和public Handler(Looper looper, Callback callback)构造方法时,会调用到如下构造方法,就是将我们传入的looper和callback赋给Handler中的成员变量:

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
复制代码

2)、当我们调用public Handler()或public Handler(Callback callback)构造方法时,会调用到如下构造方法,在注释1处可以看到通过Looper.myLooper()获取Looper并赋值为mLooper,Looper我们在下文会进行介绍:

public Handler(Callback callback, boolean async) {
    、、、
    mLooper = Looper.myLooper();//1
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
复制代码

2、Handler发送消息

当我们发送一条message时,在handler中的调用链如下:

sendMessage — sendMessageDelayed — sendMessageAtTime — enqueueMessage

最终调用到的enqueueMessage方法如下,有三步操作比较关键:

注释1处将message的target属性设为this,这个target属性有什么用呢?它在我们处理消息的时候会用到,用来标识这条msg被哪个handler处理;

注释2处会检查Handler的成员变量mAsynchronous如果为true,就将msg设置为异步消息,这个异步消息相关的知识我们在后面同步屏障的时候会讲;

注释3处就是调用了MessageQueue的enqueueMessage将msg放入消息队列;

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this;//1
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);//2
    }
    return queue.enqueueMessage(msg, uptimeMillis);//3
}
复制代码

3、Handler处理消息

我们在后面分析looper的时候会看到,从loop方法中取出的消息会分发给handler处理,也就是调用到了dispatchMessage方法;

注释1处表示如果message的callback字段不为空,则会调用handleCallback,也就是调用message的callback的run方法;

注释2处表示如果handler的mCallback字段不为空,则会调用mCallback的handleMessage方法,我们可以在创建Handler的时候传入自定义的Callback;

注释3处表示前两个条件都为空,就会调用我们在handler中自己实现handleMessage(msg)方法;

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg); //1
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) { //2
                return;
            }
        }
        handleMessage(msg); //3
    }
}
复制代码

二、MessageQueue源码解析

分析MessageQueue源码之前,我们需要知道Message的结构,Message类似于链表节点的结构,在其内部维护了一个next成员变量,类型也是Message,用于指向下一个Message;而在MessageQueue中维护了一个由链表实现的优先级队列,链表的节点就是Message;

1、enqueueMessage

在handler调用sendMessage最终调用到queue.enqueueMessage,方法如下所示;

注释1处表示队列中没有消息或者插入的消息等待时间为0或者等待时间小于队列头消息的等待时间,此时将新插入的消息放到队列头;

注释2处表示不满足这三种条件,则会在队列中插入到合适的位置;注释三处表示如果需要唤醒,则调用nativeWake;

boolean enqueueMessage(Message msg, long when) {
    、、、
    synchronized (this) {
        、、、
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) { //1
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else { //2
            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;
        }
        if (needWake) {
            nativeWake(mPtr); //3
        }
    }
    return true;
}
复制代码

2、next

分析完enqueueMessage,我们先提前分析一下next方法,这个方法在什么地方被调用呢,就是在looper的loop方法中会调用,用于取出消息,主要源码如下;

注释1处的native方法nativePollOnce用于cpu休眠,这也就跟nativeWake对上了,取消息休眠,消息入队唤醒;并且消息入队和出队是都是在synchronized (this)代码块中,并且锁的都是this对象,因此发送消息或者取消息是互斥的,每次只有一个线程能操作;

注释2处用于处理异步消息,我们在后面介绍同步屏障时再分析;注释3处用于将得到的消息返回,在返回之前需要重新维护一下队列中的message;

Message next() {
    、、、

    int nextPollTimeoutMillis = 0;
    for (;;) {            

        nativePollOnce(ptr, nextPollTimeoutMillis); //1

        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous()); //2
            }
            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;
                    msg.markInUse();
                    return msg; //3
                }
            } else {
                nextPollTimeoutMillis = -1; //4
            }

        }
        nextPollTimeoutMillis = 0; //5
    }
}
复制代码

三、Looper源码分析

我们在创建Handler时如果没有传入Looper,则会调用Looper.myLooper()获取Looper对象;我们在使用handler消息机制时,需要保证Looper已经创建并且开启loop循环来使消息机制正常运行,但是我们在主线程中使用handler时并没有进行这两步操作,那么这两步操作是在哪里执行的呢?

答案是在ActivityThread的main方法中执行,ActivityThread的main方法代表我们应用程序进程的主线程执行入口,main方法如下所示,注释1处就是创建Looper对象,注释2处就是开启loop循环;

public static void main(String[] args) {
    、、、
    Looper.prepareMainLooper(); //1
    、、、
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

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

    、、、
    Looper.loop(); //2

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

1、prepare方法介绍

在prepareMainLooper方法中调用prepare;

注释1处首先会判断,用来保证一个线程只能有一个Looper;

注释2处用来新建Looper对象,并将其设置进ThreadLocal中,保证线程隔离;quitAllowed参数表示looper是否可以退出,我们在主线程中调用的prepare将此参数传为fasle表示不可退出,我们在子线程中创建looper时会默认此参数为true表示可以退出,并且我们在子线程中用完一定要退出,否则引发内存泄漏;

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) { //1
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed)); //2
}
复制代码

通过构造方法创建Looper如下所示,注释1处表示新建MessageQueue对象,因此Looper和MessageQueue都是对应每个线程的,并且每个线程都只有一个;

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed); //1
    mThread = Thread.currentThread();
}
复制代码

ThreadLocal的set方法如下所示,这里通过getMap获得每个线程的成员变量threadLocalMap,ThreadLocal和Looper是在ThreadLocalMap中的Entry数组中保存的,从而保证了线程的隔离;这里不详细介绍,在前面介绍ThreadLocal的文章中已经介绍过了;

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
复制代码

2、loop方法介绍

介绍完了prepare,我们也就创建完了Looper和MessageQueue并且将它们绑定到了对应线程上了;下面介绍loop方法用于从messageQueue中取消息;

注释1处从MessageQueue中取消息,两种情况会造成阻塞,一种是队列中没有消息,第二种是队列中最早的消息也还没到执行时间;

注释2处用于分发消息到handler去执行;

注释3处表示消息处理完了之后进行回收;

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    、、、

    for (;;) {
        Message msg = queue.next(); // might block //1
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        、、、

        try {
            msg.target.dispatchMessage(msg); //2               
        }
        、、、

        msg.recycleUnchecked(); //3
    }
}
复制代码

3、quit方法介绍

当我们在子线程中创建了looper时,我们在用完之后一定要调用quit方法退出looper,否则会造成内存泄漏;quit方法只有一行代码,就是调用了MessageQueue的quit方法;

public void quit() {
    mQueue.quit(false); //1
}
复制代码

我们接着分析MessageQueue的quit的方法;

注释1处表示主线程的looper不能退出;

注释2处将mQuitting变量置为true,并通过注释3唤醒队列;当我们将mQuitting置为true后,在MessageQueue的next方法就会返回null,然后我们在looper的loop方法中判断如果返回的msg为null,就会退出for循环,也就退出了loop;

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

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

        nativeWake(mPtr); //3
    }
}
复制代码

4、其他方法分析

1)、getMainLooper用于返回主线程的looper;

public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}
复制代码

2)、如果我们在子线程中创建了looper,myLooper用于返回当前线程的looper;

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
复制代码

3)、myQueue用于返回当前线程对应的MessageQueue;

public static @NonNull MessageQueue myQueue() {
    return myLooper().mQueue;
}
复制代码

4)、getThread用于返回当前Looper对应的线程;

public @NonNull Thread getThread() {
    return mThread;
}
复制代码

4、Message源码分析

我们在前面也分析过,Message就是链表中的节点,内部有一个next成员变量,类型也是Message的,用于指向下一个Message;我们创建一个Message的时候一般通过new Message或者Message.obtain;

1、obtain方法解析

obtain方法如下所示,我们在Message中维护了一个Message池,sPool表示池中的第一个Message,如果sPool不为空,我们就复用sPool,并在注释2处将池中的第一个元素更新一下,并在注释3处将复用的Message的next置为null,从链表中去除;

public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool; //1
            sPool = m.next; //2
            m.next = null; //3
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}
复制代码

2、recycleUnchecked方法解析

我们在looper的loop循环中取出消息,并分发给handler处理,当消息处理完之后,会调用msg.recycleUnchecked(),这就是执行的message回收;

如下所示,首先会将Message的各个字段清空,然后在注释1处将回收的message的next指向sPool;

在注释2处将sPool重新置为回收的此message,并在注释3处维护一下池中元素的个数;

void recycleUnchecked() {
    // Mark the message as in use while it remains in the recycled object pool.
    // 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; //1
            sPool = this; //2
            sPoolSize++; //3
        }
    }
}
复制代码

五、同步屏障

同步屏障,顾名思义,就是屏蔽同步消息,处理异步消息;

在MessageQueue的next方法中有如下这么一段代码,注释1处如果判断msg不为空,然后msg.target为空,则会触发同步屏障,

在注释2处的while循环中去寻找异步消息;我们在创建消息时可以调用setAsynchronous(true)将消息设置为异步消息;那么这个msg.target == null条件是怎么满足的呢?

if (msg != null && msg.target == null) { //1
    // Stalled by a barrier.  Find the next asynchronous message in the queue.
    do {
        prevMsg = msg;
        msg = msg.next;
    } while (msg != null && !msg.isAsynchronous()); //2
}
复制代码

开启同步屏障是通过调用postSyncBarrier方法,在注释1处我们发现创建的msg并没有给target字段赋值,因此为空,所以也就满足了同步屏障的条件;然后在下面会将这条msg插入到队列中;

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;  //1

        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;
    }
}
复制代码

同步屏障用于处理一些紧急任务,我们通过开启同步屏障,然后将消息设为异步消息,我们处理完异步消息之后需要关闭同步屏障,否则在next取消息时同步消息取不出来了,那么怎么取消同步屏障呢?

取消同步屏障通过调用removeSyncBarrier,方法如下所示,我们是在注释1处或注释2处取消的同步屏障,这个p消息就是开启同步屏障的那个msg,我们通过prev.next = p.next或者mMessages = p.next将此消息从队列中移除。

public void removeSyncBarrier(int token) {
    // Remove a sync barrier token from the queue.
    // If the queue is no longer stalled by a barrier then wake it.
    synchronized (this) {
        Message prev = null;
        Message p = mMessages;
        while (p != null && (p.target != null || p.arg1 != token)) {
            prev = p;
            p = p.next;
        }
        if (p == null) {
            throw new IllegalStateException("The specified message queue synchronization "
                    + " barrier token has not been posted or has already been removed.");
        }
        final boolean needWake;
        if (prev != null) {
            prev.next = p.next; //1
            needWake = false;
        } else {
            mMessages = p.next; //2
            needWake = mMessages == null || mMessages.target != null;
        }
        p.recycleUnchecked();

        // If the loop is quitting then it is already awake.
        // We can assume mPtr != 0 when mQuitting is false.
        if (needWake && !mQuitting) {
            nativeWake(mPtr);
        }
    }
}
复制代码

六、同步屏障在系统中的典型应用

View更新时,draw、requestLayout、invalidate等很多地方都调用了ViewRootImpl#scheduleTraversals(),下面我们看看这个方法的实现,注释1处就是开启了同步屏障;注释2处的postCallback方法用于发送异步消息;

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); //1
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); //2
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}
复制代码

postCallback方法最终会调用postCallbackDelayedInternal,如下所示,注释1处表示这是一个异步消息;

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); //1
            mHandler.sendMessageAtTime(msg, dueTime);
        }
    }
}
复制代码

我们处理完异步消息需要取消同步屏障,那么是在什么时候取消的呢?答案就是ViewRootImpl#unscheduleTraversals()方法,如下所示,在注释1处就是调用了取消同步屏障的方法;

void unscheduleTraversals() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); //1
        mChoreographer.removeCallbacks(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    }
}
复制代码

七、子线程创建Looper的典型应用之IntentService

我们知道IntentService作为一种特殊的Service,可以自动在子线程执行我们重写的onHandleIntent方法,并且在执行完之后自动停止服务,我们下面分析一下如何实现的;

ServiceHandler是IntentService的内部类,在handleMessage方法中就实现了特有的逻辑,那么这个handleMessage什么时候会被调用呢?

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);
    }
}
复制代码

IntentService的onCreate和onStart方法如下所示,在注释1处创建了HandlerThread,然后在注释2处获得此线程对应的looper;注释3处通过创建的looper创建一个handler;

注释4处通过此handler发送消息,也就触发了我们上面的handleMessage方法,那么这个HandlerThread又是啥呢?我们在下一小节进行介绍。

@Override
public void onCreate() {
    super.onCreate();
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); //1
    thread.start();

    mServiceLooper = thread.getLooper(); //2
    mServiceHandler = new ServiceHandler(mServiceLooper); //3
}

@Override
public void onStart(@Nullable Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg); //4
}
复制代码

八、子线程创建Looper的典型应用之HandlerThread

HandlerThread的run方法如下所示,当我们在IntentService中调用此线程的start方法时会调用此run方法,可以看到注释1和注释3分别调用了prepare和loop,这也是在子线程创建looper的关键;注释2处的加锁用于和getLooper方法配合;

public void run() {
    mTid = Process.myTid();
    Looper.prepare(); //1
    synchronized (this) { //2
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop(); //3
    mTid = -1;
}
复制代码

getLooper方法如下所示,注释1处的锁和run方法配合,注释2表示当mLooper还为空时需要调用wait释放锁并等待,然后在run方法中给mLooper赋值之后调用notifyAll唤醒wait;

public Looper getLooper() {
    if (!isAlive()) {
        return null;
    }
    
    // If the thread has been started, wait until the looper has been created.
    synchronized (this) { //1
        while (isAlive() && mLooper == null) {
            try {
                wait(); //2
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享