Android面试刨根问底之常用源码篇(一),下血本买的

2022/2/4 14:42:59

本文主要是介绍Android面试刨根问底之常用源码篇(一),下血本买的,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

}

else if (oldThr > 0)

//通过threshold设置新数组容量

newCap = oldThr;

else {

}

if (newThr == 0) {

}

threshold = newThr;

@SuppressWarnings({“rawtypes”,“unchecked”})

//通过threshold设置table的初始容量

Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];

table = newTab;

return newTab;

}

通过以上操作,不论初始化HashMap的时候,传入的容量是多少,都能保证HashMap的容量是2x。

Handler源码分析

===========

一直在纠结一个事,因为自己不爱看大段的文字。

自己写总结的时候到底要不要贴上部分源码。

后来硬着头皮加上了,因为源码里很多东西比自己写的清楚。

RTFSC

相关概念

Handler Message MessageQueue Looper ThreadLocal

Handler机制的完整流程

  1. Message#obtain()

  2. Handler#

  3. Handler#send/post

  4. MQ#enqueueMessage() *消息的排序

  5. Looper#prepareMainLooper()

  6. Looper#prepare()

  7. ThreadLocal机制

  8. Looper#loop()

  9. MQ#next() *延迟消息的处理

  10. Handler#dispatchMessage()

Message#obtain()

message中的变量自己去看源码,target,callback,when

从handler或者是message的源码中都可以看到,生成Message的最终方法都是调用obtain。

ps:如果你非要用Message的构造方法,那么看清楚他的注释,构造方法上面的注释写的也很清楚,

/**

  • Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).

*/

public Message() {

}

下面来分析一波obtain()方法:

  1. 为什么上来就是一个同步?

任意线程都可以创建message,所以为了维护好内部的messge池,加锁

  1. sPool是个什么东西

字面上看是个池子,但是从定义上看,是一个Message。为什么还要说成一个message池呢?因为Message内部有个next变量,Message做成了一个链表的形式。这个池子怎么存储message呢?稍后分析源码。

通过读obtain()的源码,结合链表的知识,很容易理解Message中Spool的原理。

public static final Object sPoolSync = new Object();

private static Message sPool;

private static int sPoolSize = 0;

/**

  • Return a new Message instance from the global pool. Allows us to

  • avoid allocating new objects in many cases.

*/

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();

}

通过查看调用链,我们能够看到在MQ中enqueueMessage调用了recycle(),而recyle中也是通过链表的形式对sPool进行维护。源码简单易懂

下面来看下sPool是怎么维护的。

在recycleUnchecked()同样也是加了锁的。然后就是用链表的形式维护这个池子,size++

public void recycle() {

if (isInUse()) {

if (gCheckRecycle) {

}

return;

}

recycleUnchecked();

}

/**

  • Recycles a Message that may be in-use.

  • Used internally by the MessageQueue and Looper when disposing of queued Messages.

*/

void recycleUnchecked() {

synchronized (sPoolSync) {

if (sPoolSize < MAX_POOL_SIZE) {

next = sPool;

sPool = this;

sPoolSize++;

}

}

}

Handler

Handler类的源码总共不超过1000行,并且大部分都是注释,所以我们看该类源码的时候,更多的是看他的注释。静下心来看源码

  • 构造方法

  • callback对象

  • dispatchMessage

Handler发送消息(send/post)

Handler发送消息的方式分为两种:

1.post

2.send

不论是post还是send(其他方法)方式,最终都会调用到sendMessageAtTime/sendMessageAtFrontOfQueue。执行equeueMessage,最终调用MQ#enqueueMessage(),加入到MQ中。

1. post方式

以post方式发送消息,参数基本上都是Runnable(Runnable到底是什么,这个要搞懂)。post方式发送的的消息,都会调用getPostMessage(),将runnable封装到Message的callbak中,调用send的相关方法发送出去。

ps:个人简单、误导性的科普Runnable,就是封装了一段代码,哪个线程执行这个runnable,就是那个线程。

2. send方式

以send方式发送消息,在众多的重载方法中,有一类比较容易引起歧义的方法,sendEmptyMessageXxx(),这类方法并不是说没有用到message,只是在使用的时候不需要传递,方法内部帮我们包装了一个Message。另一个需要关注的点是: xxxDelayed() xxxAtTime()

1.xxxDelayed()

借助xx翻译,得知 delayed:延迟的,定时的,推迟 的意思,也就是说,借助这个方法我们能做到将消息延迟发送。e.g:延迟三秒让View消失。ps:在我年幼无知的时候,总是搞懵这个方法,不会用。

在这个方法的参数中,我们看到如果传入的是毫秒值,那么会在delayMillis的基础上与SystemClock.uptimeMillis()做个加法。然后执行sendMessageAtTime()。

SystemClock.uptimeMillis() 与 System.currentTimeMillis()的区别自己去研究。

public final boolean sendMessageDelayed(Message msg, long delayMillis)

{

if (delayMillis < 0) {

delayMillis = 0;

}

return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

}

2.xxxAtTime()

在这个方法就更简单易懂了,执行的具体时间需要使用者自己去计算。

在Handler内的equeueMessage中,第一行的msg.target = this;,将handler自身赋值到msg.target,标记了这个msg从哪来,这个要注意后面会用到

MQ#enqueueMessage()

这个方法那是相当的关键

在此之前,我们一直鼓捣一个参数delayMillis/uptimeMillis,在这个方法里参数名变为了when,标明这个message何时执行,也是MQ对Message排序存储的依据。MQ是按照when的时间排序的,并且第一个Message最先执行。

在省去了众多目前不关心的代码后,加上仅存的一点数据结构的知识,得到msg在MQ中的存储形式。

mMessages位于队列第一位置的msg,新加入到msg会跟他比较,然后找到合适的位置加入到队列中。

ps:记得在一次面试中,面试官问到延迟消息的实现思路,我照着源码说了一下。但是被问到:**每次新加入消息,都要循环队列,找到合适的位置插入消息,那么怎么保证执行效率。**我不知道他这么问是想考我优化这个东西的思路,还是他觉得我说错了。就犹豫了一下,没有怼回去。

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) {

msg.next = p;

mMessages = msg;

needWake = mBlocked;

} else {

needWake = mBlocked && p.target == null && msg.isAsynchronous();

Message prev;

for (;



这篇关于Android面试刨根问底之常用源码篇(一),下血本买的的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程