2024年安卓最全Android面试题精选——再聊Android-Handler机制-1,安卓资深工程师面试题

最后

感觉现在好多人都在说什么安卓快凉了,工作越来越难找了。又是说什么程序员中年危机啥的,为啥我这年近30的老农根本没有这种感觉,反倒觉得那些贩卖焦虑的都是瞎j8扯谈。当然,职业危机意识确实是要有的,但根本没到那种草木皆兵的地步好吗?

Android凉了都是弱者的借口和说辞。虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

所以,最后这里放上我耗时两个月,将自己8年Android开发的知识笔记整理成的Android开发者必知必会系统学习资料笔记,上述知识点在笔记中都有详细的解读,里面还包含了腾讯、字节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

以上全套学习笔记面试宝典,吃透一半保你可以吊打面试官,只有自己真正强大了,有核心竞争力,你才有拒绝offer的权力,所以,奋斗吧!骚年们!千里之行,始于足下。种下一颗树最好的时间是十年前,其次,就是现在。

最后,赠与大家一句诗,共勉!

不驰于空想,不骛于虚声。不忘初心,方得始终。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

**2、Looper负责管理MessageQueue:**Looper会不断地从MessageQueue取出消息,交给Handler处理。
3、MessageQueue是消息队列(实时上它是用链表实现的),负责存放Handler发送过来消息。
**4、一个Looper对应一个线程(自己所在的线程,如:线程B)。**Looper的loop()方法运行在自己所在的线程(线程B)中,当Handler在线程A发送一条消息存放到MessageQueue时,Looper的loop()方法在线程B把消息取出来,并交给Handler处理,所以Handler的处理消息的方法是运行在Looper所在的线程(线程B)的。由于多个线程之间共享内存空间,所以Handler可以在线程A把消息存放到MessageQueue,Looper可以在线程B把消息取出来,一存一取之间就实现了线程的切换。

现在我们了解了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)方法。

总结

首先是感觉自己的基础还是不够吧,大厂好像都喜欢问这些底层原理。

另外一部分原因在于资料也还没有看完,一面时凭借那份资料考前突击恶补个几天居然也能轻松应对(在这里还是要感谢那份资料,真的牛),于是自我感觉良好,资料就没有怎么深究下去了。

之前的准备只涉及了Java、Android、计网、数据结构与算法这些方面,面对面试官对其他基础课程的考察显得捉襟见肘。

下一步还是要查漏补缺,进行针对性复习。

最后的最后,那套资料这次一定要全部看完,是真的太全面了,各个知识点都涵盖了,几乎我面试遇到的所有问题的知识点这里面都有!希望大家不要犯和我一样的错误呀!!!一定要看完!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值