Handler机制

Handler 机制

前言

很早以前,就研究过Handler机制源码,但当时并没有用文字记录。以至于工作中被同事、同行问起,只能回忆起大概,也就不好意思说自己懂它了。正好今天没事儿,重新撸一撸源码,以来丰富博客,而来说不定有新的收获呢!

看完这篇博文,能得到什么

  • 脱了衣服的Handler。
  • 你也可以从我的分析过程中了解到,怎样脱掉Handler衣服的方法。

Handler机制的由来

现在的手机屏幕的刷新频率基本为60Hz,也就是一秒撸60次(这手速!),平均16.67毫秒/次。所以想要APP所有的内容都显示出来,也就是不丢帧,就得保证主线程执行时长不超过16毫秒。所以,超过这个时常的逻辑,就别放到主线程了。即使一个刚入Android坑的同学,也知道IO读写,网络等耗时逻辑必须放在子线程中处理,相信你也不会在这上边犯错。

那么,问题来了,主线程通常也被称为UI线程,它的作用就是用来创建并且更新UI的。我们在子线程中逻辑执行的结果,比如我们从网络上获取一张图片之后,怎么让主线程去显示这张图片呢?没错,Android提供了Handler来解决线程之间的通信问题。

源码解读

  • 首先,我们来看看Hanlder的一般用法。

创建Handler对象,并复写handleMessage方法

  Handler mHandler = new Handler() {//创建一个Handler
        @Override
        public void handleMessage(Message msg) {//复写handleMessage方法
            //处理对应逻辑
        }
  };

发送消息给刚才创建的mHandler的对象处理。发送过后,handleMessage函数将被调用。这就是一次简单的Handler的调用。

Message message = Message.obtain();
message.what = 1;
mHandler.sendMessage(message);  
  • 相关类
    • 明面上,我们可以拿到两个类:Hanlder、Message。
    • 其实,整套机制还包含Looper、和MessageQueue两个不可见的对象。
  • 走进源码

    接下来,我们就围绕Handler、Message、Looper以及MessageQueue来探索Handler中的奥秘吧。

    1. 首先,来看看mHandler.sendMessage(message)做了什么。

      代码一路跟踪,最终到了Handler中的一个函数

      public boolean sendMessageAtTime(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(本文中也会称为‘消息队列’)也就是queue, 从if条件判断可以看出,queue在这之前必然已经初始化(别问为什么)。既然如此,我们来开个小差,看看给queue赋值的mQueue什么时候初始化的。代码依然在Handler中。

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

擦,捅了马蜂窝了,代码这么多。我就看个初始化而已,至于吗。哎,我们继续。这是Handler的构造函数之一,我们的new Handler()会调用该构造完成对象创建动作。在该构造函数中,我们看到了我们的目标mQueue得到了初始化mQueue = mLooper.mQueue;,好吧,mLooper开始出来搞事情了。可以看出,mLooper是通过Looper对myLooper函数静态调用赋值的mLooper = Looper.myLooper();。进入Looper代码。

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

进一步,我们发现,myLooper函数的返回值实际上是通过sThreadLocal.get()获得的线程本地变量副本。所以,该变量副本肯定是通过sThreadLocal.set();赋值的(关于ThreadLocal原理大家可自行百度)。代码依然在Looper中,查看代码,果然。

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));
}
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

看到没sThreadLocal.set(new Looper(quitAllowed));说什么来着。代码看到这里,不知道大家还记不记得,跟踪到这里是为啥。是为了查看mQueue的初始化过程。
简单梳理一下。

  1. 在Handler.sendMessage的调用链中,我们看到了发送消息之前,有一个叫queue的MessageQueue必须初始化。
  2. queue的初始化mQueue = mLooper.mQueue;,mLooper = Looper.myLooper();引出了Looper。
  3. Looper.myLooper()的返回值,实际上是ThreadLocal中保存的Looper变量副本。
  4. 结合上面一段代码,可以看出,Looper变量副本的保存,是通过调用Looper.prepare()保存的。调用的同时,会新建一个Looper对象。而创建Looper对象的构造函数时,new了一个MessageQueue对象并且保存到了变量mQueue中。这个mQueue正式我们最开始发现的消息队列。

好了,代码看到这里,我们只需要知道Looper.prepare()方法在哪里调用就能完成这一步的探索。通过搜索源码,ActivityThread.java。可以看到如下代码

public static void main(String[] args) {
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

这部分代码片段来自于Nougat 7.1.1_r6,可以看出Activity主线程的主函数中,调用了Looper.prepareMainLooper();,看起来是不是很像Looper.prepare()?大胆猜测一下,就是这个函数。代码回到Looper,找到prepareMainLooper();

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

果然prepareMainLooper()中调用了prepare()方法。至此MessageQueue的出事话过程就全明白了。
1. MessageQueue在UI线程中,早已经通过调用Looper.prepareMainLooper()创建了一个Looper对象,同时也创建了一个MessageQueue对象并使用变量mQueue保存在该Looper中。

明白消息队列初始化过程,是一个很好的开始。

public boolean sendMessageAtTime(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);
}

还记得最开始贴出的代码吗,对,就是Handler.sendMessage()引出的。这段代码,还有一个比较关键的是enqueueMessage(),字面意思是加入消息队列。我们可以大胆猜测,这个函数就是要将发送的消息,加入到之前初始化的消息队列中。来看代码。

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

简单来看,该函数做了两件事情。

  1. msg.target = this;使当前发送的消息对象msg.target保持对当前Handler对象的引用。这一部分对于Handler机制非常重要。
  2. 调用之前的消息队列queue.enqueueMessage()将消息插入。进一步来看看这个方法。
boolean enqueueMessage(Message msg, long when) {
    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) {
    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;
    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;
}

哇塞,好多代码。在使用handler过程中,不知道大家有没有想过这样一个问题,Handler机制中有一个消息队列,里边可以存放很多消息对象。那么就必然需要用一种数据结构来维护,那这是个什么样的数据结构呢?我刚开始猜测不是数组,就是集合。于是,我就在源码里拼命的找啊,结果屁都没找到。显然,GooGle大神们想的和我等屁民并不一样。代码就在上边,为了突出,专门贴出来,见识一下吧。

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

这段代码其实很简单。假设现在有三条消息msg1,msg2,msg3。这段代码将这三条消息彼此关联起来,形成了一种单向链表的结构。

  1. msg1 是队首消息,应该本第一个发送,或者说第一个轮询。
  2. msg1.next = msg2; msg1.next记录了下一个将要轮询的消息msg2。
  3. msg2.next = msg3; msg2.next记录msg2的下一个需要轮询的消息为msg3。
    大神就是大神,我等屁民只好学着点了。代码非常简单,怕就怕“我怎么想不到”。
    好了,代码就看完了……欸,不对。初始化了Looper,MessageQueue,也通过enquenMessage将消息加入到了队列中。然后呢?消息是怎么发送到handlerMessage中的呢?
    细心的同学应该发现,在ActivityThread的主函数中,调用Looper.prepareMainLooper后,接着就调用了Looper.loop()(可以回去查看一下这部分代码);那么是不是,prepare之后就必须调用loop呢?是的,在prepare函数的源码注释中可以看到:Be sure to call * {@link #loop()} after calling this method。可见loop()函数是Handler机制中不可或缺的一环。那么就来看看loop到底做了啥,让它可以这么傲娇。
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;
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        msg.recycleUnchecked();
    }
}

我们来细细读一下这段代码(去除了和本文主题无关部分)。
* Message msg = queue.next();获取消息链表中的队首消息。next()函数就不多介绍了,和入队时形式差不多,不过这个方法是从消息队列中获取message。
* msg.target.dispatchMessage(msg);调用enqueueMessage方法时,msg.target记录的Handler对象,调用其dispatchMessage函数。来看看这个函数。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

代码显而易见,直接调用了handleMessage函数,这就是为啥我们创建Handler时需要重写handleMessage方法的原因。
最后,总结一下。

Message:

既是消息对象,也是消息队列的重要组成部分。前一个Message.next记录了后一个Message对象。以此为基础形成了一种类似链表的数据结构。

MessageQueue:

消息队列。自己并不是队列本身,更像是Message组成队列的一个维护者。它通过enqueueMessage(),以及next()函数控制消息的入队和出队达到维护的目的。

Handler: 有两个比较重要的方法。

sendMessage(): 发送的消息实际上只是被加入了Message的队列中。同时发送的消息对象会通过Message.target记录调用者,也就是调用该函数的Handler本身。

handleMessage(): 要想用Handler,这个函数必备重写。因为Looper在轮询消息队列时会通过Message.target.handleMessage(),调用这个函数,达到线程间消息通讯的目的。

Looper:

Looper在UI线程中会默认调用Looper.prepareMainLooper();该函数的调用,会创建一个Looper对象,以及一个MessageQueue对象。MessageQueue对象会保存在Looper对象的mQueue字段中,方便调用。

Looper.prepareMainLooper()调用之后,接着就会调用Looper.loop()。该函数主要从Message队列中通过for循环不断的获取消息,且不断的调用dispatchMessage最终调用到handleMessage。达到线程间通讯的目的。

子线程发消息到主线程

附送代码,不解释

public class HandlerActivity extends Activity{
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.handler_activity);
        thread.start();
    }
    public void send(View view) {//Button的点击事件
        threadHandler.sendEmptyMessage(1);
    }
    Handler threadHandler;
    Thread thread = new Thread() {
        @Override
        public void run() {
            Looper.prepare();
            threadHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    Toast.makeText(HandlerActivity.this, "88888", Toast.LENGTH_SHORT).show();
                }
            };
            Looper.loop();
        }
    };
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值