FrameLayout measure过程源码Log全解析之五:onMeasure第一部分之MeasureSpec类的bit-mask

本篇我们将讨论MeasureSpec类,搞清楚size和mode是怎样储存以及怎样提取的。

我们来看FrameLayout.java的第二句

        final boolean measureMatchParentChildren =
                MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
                MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
这里涉及到MeasureSpec类,这个类是View.java里的内部静态类,成员变量如下

   private static final int MODE_SHIFT = 30;
   private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
   public static final int UNSPECIFIED = 0 << MODE_SHIFT;
   public static final int EXACTLY     = 1 << MODE_SHIFT;
   public static final int AT_MOST     = 2 << MODE_SHIFT;
这里有三种mode(UNSPECIFIED, EXACTLY, AT_MOST),然后还定义了MODE_SHIFT和MODE_MASK,我们来看看具体MASK的作用和过程是什么。来看makeMeasureSpec这个函数

public makeMeasureSpec(size, mode) {
    if (sUseBrokenMakeMeasureSpec) {
         return size + mode;
    } else {
         return (size & ~MODE_MASK) | (mode & MODE_MASK);
    }
}
第一部分size + mode是在旧版本上的处理方式,有bug(没考证),所以出现了新版本的处理方式。用了bit-wise mask,我们来详细看看这个mask的原理

bit-mask原理

有两个数字(二进制),a, b,我想要得到一个新数字c,其中c的某2,3位由b决定,剩余的位由a决定,比如:

a = 10 10 10    // 已知
b = 01 01 11    // 已知
c = 10 01 10    // 要求,注意c的2, 3位来自于b, 其余来自于a
如何做到呢?我们先定义一个mask,因为要2, 3位,所以这两位为1,其余为0. 

m = 00 11 00    //  mask 
做如下操作就可以得到c

(a & ~m) | (a & m)
分解一下,先看第一部分

a & ~m =   10 10 10
        &  11 00 11         // &~m的意思是,去掉2,3位,其余都留下
        ---------------- 
           10 00 10         // a除了2,3位,其余被保留下来了
再看第二部分

b & m  =   01 01 11
        &  00 11 00         // &m的意思是,只要2,3位,其余都丢掉
        ---------------- 
           00 01 00         // b只有2,3位被保留下来了
然后合起来
(a & ~m) | (b & m)  =   10 00 10
                     |  00 01 00         // |的意思是,加在一起,因为不同位被mask掉了,所以不用考虑进位问题
                     ---------------- 
                        10 01 10         // 和上面要求的c的值一样

将size和mode进行bit-mask

framework从效率考虑,想让MeasureSpec类建立对象,所以通过bit-mask的方法将size和mode储存为一个唯一的int,然后要用的时候再反算回来。我们看下是怎么反算回size和mode的。

int getMode(int measureSpec) {return (measureSpec & MODE_MASK);}
int getSize(int measureSpec) {return (measureSpec & ~MODE_MASK);}
不过通过这样的操作真的可以把原本的数字反算回来吗?不行的,如果不相信可以用上面的a,b,c,m的例子算一下。是无法从c和m算回a和b的。

framework这样算之所以是可行的和MASK_SHIFT有关

private static final int MODE_SHIFT = 30;
mode被移动了30位....相当之大。以下括号里位30位及以内的位数

   private static final int MODE_MASK  = 0x3 << MODE_SHIFT;   // 11 (000...) 
   public static final int UNSPECIFIED = 0 << MODE_SHIFT;     // 00 (000...)
   public static final int EXACTLY     = 1 << MODE_SHIFT;     // 01 (000...)
   public static final int AT_MOST     = 2 << MODE_SHIFT;     // 10 (000...)

而size是没有位移的,size被转为二进制之后,根本无法填满30位这么多,所以

(size & ~MODE_MASK) | (mode & MODE_MASK);
的含义是,让mode决定30以上的位数,让size决定30位以下的位数,他们不会冲突,因为size转为二进制后无法达到30位以上(谁这么丧心病狂用这么大的屏幕)。

我们来做一个简化的操作

MODE_MASK = 11 00 00         // framework设定
mode      = 01 00 00         // framework设定, EXACTLY
size      = 00 00 01         // 假设的数字



那么

size & ~MODE_MASK =   00 00 01
                   &  00 11 11
                   --------------
                      00 00 01

 然后
mode & MODE_MASK  =   01 00 00
                   &  11 00 00
                   --------------
                      01 00 00
最后

measureSpec = (size & ~MODE_MASK) | (mode & MODE_MASK)
            =   00 00 01   // 30位及以下由size决定
             |  01 00 00   // 30位以上由mode决定
            -------------
                01 00 01   // 因为size很小,所以没有冲突,这里算假设很大了
然后反过来算mode

measureSpec & MODE_MASK =   01 00 01
                         &  11 00 00              // 30位及以下的值被mask去掉了
                         ----------- 
                            01 00 00              // mode的值出来了
算mode

measureSpec & ~MODE_MASK =   01 00 01
                          &  00 11 11              // 30位以上的值被~mask去掉了
                          ----------- 
                             00 00 01              // size的值出来了
可以看到,之所以能算出来还是因为位移后的mode和size大小差距太大,所以互不影响。妙啊!!都是什么神人想出来的。

剩下的两个函数比较直接

static int adjust(int measureSpec, int delta)   // 把measureSpec直接加上delta大小,因为mode太大,所以只影响到了size,相当于调整了size
public static String toString(int measureSpec)  // 把mode数字转为UNSPECIFIED, EXACTLY或AT_MOST,size直接显示出来

下篇预告

接下来我们来看看mode的三种情况的含义,怎样确定使用哪种mode,和xml中设定的wrap_content, match_parent的关系又是怎样。

----------------------------------------------------------------------------

FrameLayout measure过程源码Log全解析系列


你可能感兴趣的:(FrameLayout measure过程源码Log全解析之五:onMeasure第一部分之MeasureSpec类的bit-mask)