Android面试题精选——再聊Android-Handler机制

53 阅读5分钟

现在我们了解了Handler的工作流程和线程切换原理。那么它在源码中又是如何去实现的呢?

从使用的角度看,我们要使用Handler首先要得到一个Handler对象,那么我们就从最简单的new Handler()作为入口,来分析它的源码。

public Handler() { this(null, false); }

public Handler(Callback callback, boolean async) { //获取Looper对象 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } //获取Looper对象的mQueue属性,mQueue 就是MessageQueue对象。 mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }

在Handler的构造方法中,首先通过Looper.myLooper()方法获取当前线程的Looper对象,如果Looper对象为空,就抛出异常,说当前线程还没有调用Looper.prepare()方法。如果Looper不为空,Handler就会持有Looper的MessageQueue对象mQueue。

我们再看Looper.myLooper()和Looper.prepare()两个方法:

static final ThreadLocal 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)); }

//获取当前线程的Looper对象 public static @Nullable Looper myLooper() { return sThreadLocal.get(); }

这里有一个很关键的类:ThreadLocal,它一个线程内部的数据存储类,通过它存储的数据只有在它自己的线程才能获取到,其他线程是获取不到的。所以sThreadLocal.get()获取的就是当前线程的Looper对象。在Looper.prepare()方法中我们看到了如果当前线程已经有Looper对象,就会抛出异常,说一个线程只能创建一个Looper对象,所以Looper对象与自己所在的线程是相对应的。 再看Looper的构造方法:

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

Looper的构造方法是私有的,外界不能直接创建Looper对象,只能通过Looper.prepare()方法创建对象并且通过Looper.myLooper()获取对象,这就保证了一个线程只能有一个Looper对象。Looper.prepare()不能调用两次。

在Looper的构造方法中会创建一个MessageQueue对象,这个就是负责存放消息的消息队列,也就是Handler所持有的mQueue 对象。它是由Looper创建和管理的。

看完了Handler、Looper和MessageQueue对象的创建,接着看消息的发送:

Handler发送消息的方法有很多,但无论你是send一个Message还是post一个Runnable;无论你是延时发送还是不延时发送,最终都会调用Handler的enqueueMessage()方法。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { //把this赋值给msg的target属性,this就是Handler对象。 msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } //把消息存放到MessageQueue return queue.enqueueMessage(msg, uptimeMillis); }

这里直接把消息存放到MessageQueue 就完事了。那么消息又是从哪里被取出来的呢? Looper里有一个Looper.loop()方法,我们看一下它的源码。

public static void loop() {

final MessageQueue queue = me.mQueue; //一个死循环 for (;;) { //从MessageQueue中取出一条消息 Message msg = queue.next(); if (msg == null) { // No message indicates that the message queue is quitting. return; } //把消息交给Handler处理。 msg.target.dispatchMessage(msg); } }

从上面的代码中我们看到loop()会开启一个死循环,不断地从MessageQueue中取出消息并交给Handler处理。在前面的enqueueMessage()方法中我们知道了msg.target就是发送消息的Handler对象。

这里有同学可能会有疑问:上面的代码中明明如果(msg == null),就退出方法,为什么我还说loop()里面是个死循环呢?这是因为MessageQueue的next()方法取出消息的时候,如果没有消息,next()方法会阻塞线程,直到MessageQueue有消息进来,然后取出消息并返回。所以queue.next()一般不会返回null,除非调用Looper的quit()或者quitSafely()方法结束消息轮询,queue.next()才会返回null,才会结束循环。

public void quit() { mQueue.quit(false); } public void quitSafely() { mQueue.quit(true); }

最后我们来看 一下消息的处理:Handler的dispatchMessage(msg)方法。

public void dispatchMessage(Message msg) { //如果Message有自己的callback,就由Message的callback处理 if (msg.callback != null) { handleCallback(msg); } else { //如果Handler有自己的mCallback,就由Handler的mCallback处理 if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } //默认的处理消息的方法 handleMessage(msg); } }

处理消息的方法有三个:

1、优先级最高的是Message自己的callback,这是一个Runnable对象,我们用Handler post一个Runnable的时候,其实就是把这个Runnable赋值个一个Message对象的callback,然后把Message对象发送给MessageQueue。 2、优先级第二的是Handler自己的mCallback,在我们创建一个Handler对象的使用可以传一个Handler.Callback对象给它并实现Callback里的handleMessage(msg)方法,作为我们的消息处理方法。 3、优先级最低的是Handler自己的handleMessage(msg)方法,这也是我们最常用的消息处理方法。

如何做好面试突击,规划学习方向?

面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。

学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。

同时我还搜集整理2020年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节

image

在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。

image

点击:《Android架构视频+BAT面试专题PDF+学习笔记》即可免费获取~

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。