接 QueueWork
在看 QueueWork 源码的时候,故意忽略掉了 debug 的相关代码,但是 ExponentiallyBucketedHistogram 这个类名看着还是挺吓人的。如果什么东西看着很可怕,让我们不敢接近,那我们就农村包围城市,逐渐了解它,知道发出那声感叹:原来没什么呀!!
先从字面理解
Exponentially 是以指数方式地,成倍地
Bucketed 桶
Histogram 直方图,柱状图
额,还不知道是什么东西,会生成直方图?
### 看看怎么用的
1 | private final static ExponentiallyBucketedHistogram |
发现用法实在是简单,只是在每次处理完任务后,把处理事件添加到这个直方图中,在达到一定条件的时候让这个直方图打印一下日志。
直面 ExponentiallyBucketedHistogram
还是要先看看累的注释的:
这个类是一个直方图,直方图的值都是正数,每个柱是前一个柱的2倍。
表示不太理解
有个属性是整数数组,在构造函数里初始化。
构造函数
这个构造函数够我们好好学习一会了。
1 | public ExponentiallyBucketedHistogram(@IntRange(from = 1, to = 31) int numBuckets) { |
首先 @IntRange 是包 android.annotation 中的,这些注释很有用,在 Android Studio 里,在我们传的值不符合规则时,会标出黄线予以警告。这里 @IntRange 声明了参数 numBuckets 取值范围为 1 ~ 31。
接下来,Preconditions 这个类也是一个 Android 内部使用的类,根据名字“先决条件”,可以知道,是做先验检查。这个语句会在检查参数符合要求好正常返回,否则抛出异常。你可能会疑惑,之前的注解不就干这事情吗?并不是,注解只是能让 AndroidStudio 显示提示,程序员在写代码时,可能会忽视它,而这里,直接抛异常,让你无法忽略。而且,注解是在编译时给提醒,在编代码的过程中,就能提示。而这个先验检查则需要在运行时,如果大家都很关注注解的提示,也就不需要这个先验检查了。
add
1 | public void add(int value) { |
负责添加熟知,这里的 value,在 QueueWork 传进的处理一次任务花费的时间。根据注释的说明,在看代码,可以知道,当值小于等于0时都放到第一柱里,否则,否则就比较复杂了。首先得搞清楚 Integer.numberOfLeadingZeros 是个什么鬼? 看文档发现是看一个无符号整数在二进制是开头有多少个0。这个代码也是很奇妙呀:
1 | int n = 1; |
>>> 是无符号数右移, 使用了二分发,有兴趣的可以研究一下。numberOfLeadingZeros 的注释中说
ceil(log2(x)) = 32 - numberOfLeadingZeros(x - 1),而在这个代码里,就跟等式右边类似的表达式,这样基本可以知道,那条语句的意思,对于添加的一个值,求他的log2,如果值比数组的最大的桶下标大,就取下表最大值。
而一个整形值,32位,这就是那个幻数的来历了,还记的构造函数的取值范围吗? 1 ~ 31,也出于这里的原因。为什么呢?
32 位用 log2 来划分,实际有33中取值,0 ~ 32,但是这分桶是看区间,所以是 1 ~ 31。其实细节还是不是很清楚,不过这时候我基本已经理解这个柱状图大体是个怎么个样子了。
reset
本来想去看 log() 原来,还有个 reset 方法,虽然很简单,但是也涨了只是,
1 | Arrays.fill(mData, 0); |
如果以前我要做这工作,可能会自己遍历来了。实际这个方法也是想我要做的一样遍历,不过写起来方便嘛。Arrays 里有很多方便的方法,类似的还有 Strings,Collections
log
log方法用文字来表达了上面图像的内容,使用 StringBuilder 来构建字符串。输出格式类似这样:
1 | hello[<1: 200, <2: 300, <4: 100, >=4: 800] |
到此为止
- 最近在背英语单词呢,又学到了两个单词
- Arrays.fill(mData, 0) 填充一个数组
- numberOfLeadingZeros() 返回一个整数的二进制时的高位的0的个数