Android Handler 解析

1 源码分析

文章根据享学课堂资料整理所得,旨在交流分享技术知识,侵删

1.1 Handler机制相关类
  • Handler:发送和接收消息
  • Looper:用于轮询消息队列,一个线程只能有一个Looper
  • Message:消息实体
  • MessageQueue:消息队列
1.2 创建Looper

创建Looper的方法是调用了Looper.preapre()方法

  • 通常我们认为ActivityThread就是主线程
    ActivityThread.java
public static void main(String[] args) {
    Looper.prepareMainLooper();

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

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

    Looper.loop();
}
  • 创建MessageQueue以及Looper与当前线程的绑定
    Looper.java
public static void prepare() {
    prepare(true);
}

// quitAllowed标识消息队列能否被销毁
private static void prepare(boolean quitAllowed) {
    // 每个线程只能创建一个Looper
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
1.3 Looper.loop()
public static void loop() {
    final Looper me = myLooper();

    //这是一个死循环,从消息队列不断的取消息
    for (;;) {
        if (!loopOnce(me, ident, thresholdOverride)) {
            return;
        }
    }
}

private static boolean loopOnce(final Looper me,final long ident, final int thresholdOverride) {
    Message msg = me.mQueue.next();
    // msg.target就是绑定的Handlerd
    msg.target.dispatchMessage(msg);
}
1.4 创建Handler
public Handler(boolean async) {
    this(null, async);
}

public Handler(@Nullable Callback callback, boolean async) {
    mLooper = Looper.myLooper();
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
1.5 创建Message

    可以直接new Message 但是有更好的方式 Message.obtain。因为可以检查是否有可以复用的Message,用过复用避免过多的创建、销毁Message对象达到优化内存和性能的目地。

public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;

    return m;
}

//sPool是就是handler dispatchMessage 后 通过recycleUnchecked回收用以复用的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();
}
1.6 Message和Handler的绑定

    创建Message的时候可以通过 Message.obtain(Handler h) 这个构造方法绑定。
所有发送Message的方法都会调用enqueueMessage方法入队,所以在创Message的时候是可以不绑定的。

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);
}
1.7 Handler发送消息

Handler发送消息的重载方法有很多,主要有两种:

  • sendMessage方法通过一系列重载方法的调用,继续调用messageQueue的enqueueMessage方法,将消息保存在了消息队列中,而最终由Looper取出,交给Handler的dispatchMessage进行处理。
        我们可以看到在dispatchMessage方法中,message中callback是一个Runnable对象,如果callback不为空,则直接调用callback的run方法,否则判断mCallback是否为空,mCallback在Handler构造方法中初始化,在主线程通直接通过无参的构造方法new出来的为null,所以会直接执行后面的handleMessage()方法。
// 以sendEmptyMessage为例
sendEmptyMessage
    -> sendEmptyMessageDelayed
        -> sendMessageDelayed
            -> sendMessageAtTime
                -> enqueueMessage
                    -> dispatchMessage

public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
  • post方法将Runnable封装成Message对象通过sendMessage发送
1.8 Handler处理消息
  • handleCallback(Message message)执行Message的callback
  • 执行Handler.mCallback的handleMessage(Message msg)或自己实现的handleMessage(Message msg)
1.9 小节

    handler.sendMessage 发送消息到消息队列MessageQueue,然后looper调用自己的loop()函数带动MessageQueue从而轮询messageQueue里面的每个Message,当Message达到了可以执行的时间的时候开始执行,执行后就会调用message绑定的Handler来处理消息。
handler机制就是一个传送带的运转机制。

  • MessageQueue就像履带。
  • Thread就像背后的动力,就是我们通信都是基于线程而来的。
  • 传送带的滚动需要一个开关给电机通电,那么就相当于我们的loop函数,而这个loop里面的for循环就会带着不断的滚动,去轮询messageQueue
  • Message就是 我们的货物了。

2 难点问题

2.1 线程同步问题

    Handler是用于线程间通信的,但是它产生的根本并不只是用于UI处理,而更多的是handler是整个app通信的框架,大家可以在ActivityThread里面感受到,整个App都是用它来进行线程间的协调。Handler既然这么重要,那么它的线程安全就至关重要了,那么它是如何保证自己的线程安全呢?
    Handler机制里面最主要的类MessageQueue,这个类就是所有消息的存储仓库,在这个仓库中,我们如何的管理好消息,这个就是一个关键点了。消息管理就2点:1)消息入库(enqueueMessage),2)消息出库(next),所以这两个接口是确保线程安全的主要档口。
enqueueMessage.java

boolean enqueueMessage(Message msg, long when) {
    synchronized (this) {
        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;
        }
    }
}

消息队列MessageQueue的实现类似于链表,并且链表是按照when按照从小到大排序的,when即执行延时.enqueueMessage中会将当前Message按照when插入到合适的位置。

    这个锁,说明的是对所有调用同一个MessageQueue对象的线程来说,他们都是互斥的,然而,在我们的Handler里面,一个线程是对
应着一个唯一的Looper对象,而Looper中又只有一个唯一的MessageQueue(这个在上文中也有介绍)。所以,我们主线程就只有一
个MessageQueue对象,也就是说,所有的子线程向主线程发送消息的时候,主线程一次都只会处理一个消息,其他的都需要等待,
那么这个时候消息队列就不会出现混乱。
    另外再看next函数

Message next() {
    for (;;) {
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            if (msg != null) {
                if (prevMsg != null) {
                    prevMsg.next = msg.next;
                } else {
                    mMessages = msg.next;
                }
                return msg;
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }   
        }     
    }    
}

next方法根据when从队列中选取下一个需要分发的Message

    next函数很多同学会有疑问:我从线程里面取消息,而且每次都是队列的头部取,那么它加锁是不是没有意义呢?答案是否定的,我们必须要在next里面加锁,因为,这样由于synchronized(this)作用范围是所有 this正在访问的代码块都会有保护作用,也就是它可以保证 next函数和 enqueueMessage函数能够实现互斥。这样才能真正的保证多线程访问的时候messagequeue的有序进行。

2.2 消息机制之同步屏障

    同步屏障的概念,在Android开发中非常容易被人忽略,因为这个概念在我们普通的开发中太少见了,很容易被忽略。大家经过上面的学习应该知道,线程的消息都是放到同一个MessageQueue里面,取消息的时候是互斥取消息,而且只能从头部取消息,而添加消息是按照消息的执行的先后顺序进行的排序,那么问题来了,同一个时间范围内的消息,如果它是需要立刻执行的,那我们怎么办,按照常规的办法,我们需要等到队列轮询到我自己的时候才能执行哦,那岂不是黄花菜都凉了。所以,我们需要给紧急需要执行的消息一个绿色通道,这个绿色通道就是同步屏障的概念。
同步屏障是什么?
    屏障的意思即为阻碍,顾名思义,同步屏障就是阻碍同步消息,只让异步消息通过。如何开启同步屏障呢?如下而已:
MessageQueue#postSyncBarrier

public int postSyncBarrier() {
    return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
    synchronized (this) {
        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()方法可以看到,Message 对象初始化的时候并没有给 target 赋值,因此,target == null的来源就找到了。上面消息的插入也做了相应的注释。这样,一条target == null的消息就进入了消息队列。
    有了同步屏障的存在,个异步消息可以被优先处理,同步消息则不会被处理。那么这些同步消息什么时候可以被处理呢?那就需要先移除这个同步屏障,即调用removeSyncBarrier()。
同步消息的应用场景
    似乎在日常的应用开发中,很少会用到同步屏障。那么,同步屏障在系统源码中有哪些使用场景呢?Android 系统中的UI更新相关的消息即为异步消息,需要优先处理。

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

// 调用链
postCallback
    -> postCallbackDelayed
        -> 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) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        mChoreographer.removeCallbacks(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    }
}
2.3 小结

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值