BitMASK使用指北

达芬奇密码2018-07-12 09:44

话说之前看自定义控件,看View的事件分发,在类的常量定义时候,经常看到一大长传的bit数,不仅如此,在MotionEvent中还有不少对bit数进行逻辑与或非操作的情况。bit操作的接触可能可以追溯到单片机和汇编语言,没想到在Android中也能再看到,这里就再学习学习Bit数在Android中的新运用。

   @Deprecated
    public static final int ACTION_POINTER_1_DOWN   = ACTION_POINTER_DOWN | 0x0000;

    /**
     * @deprecated Use {@link #ACTION_POINTER_INDEX_MASK} to retrieve the
     * data index associated with {@link #ACTION_POINTER_DOWN}.
     */
    @Deprecated
    public static final int ACTION_POINTER_2_DOWN   = ACTION_POINTER_DOWN | 0x0100;
    /**
     * Use with {@link #focusSearch(int)}. Move focus down.
     */
    public static final int FOCUS_DOWN = 0x00000082;

    /**
     * Bits of {@link #getMeasuredWidthAndState()} and
     * {@link #getMeasuredWidthAndState()} that provide the actual measured size.
     */
    public static final int MEASURED_SIZE_MASK = 0x00ffffff;

bit操作

bit位只有0|1两种状态,基本操作方法如下。

  • 求反(~)

~ 0000 1010
= 1111 0101

  • 与(&)

  0101 1010
& 0000 1100
= 0000 1000

  • 或(|)

  0101 1010
| 0000 1100
= 0101 1110

  • 左移(<<)和右移(>>)

0000 1010 << 1 = 0001 0100; 
0000 1010 >> 1 = 0000 0101;

BitMask

BitMask并不是一个类,也不是某种特殊的单位,它更像是一种思想。在BitMask中,使用一个数值来记录各种状态的集合,使用这个数值的每一位来表达每一种状态。在Android中,一个普通的int类型,是32位,则可以表达32中不同的状态而互不影响。

结合例子来说明,对于一个int类型,32种状态已经太多,这里为了方便,使用后四位bit,利用其中三种状态来表示。我们上班有三种状态:上班ing、回家、请假,用bitmask表示如下:

    public static final int AT_WORK = 0x01;
    public static final int GO_HOME = 0x01 << 1;
    public static final int TAKE_OFF = 0x01 << 2;

可以看到我们利用了其中三个bit位来代表三个状态(当然也可以使用你想的任何bit)。

ok,我们再定义一个初始状态:

private int personState = 0;

早九点,我们上班了,于是可以:

personState |= AT_WORK;//不考虑其他bit,或操作会把AT_WORK的状态位置为1;

晚九点,我们回家了,于是可以:

personState |= GO_HOME;

(这里说明下,例子举得不是特别好,因为上班、下班、请假三个状态可能会存在互斥)

某天我们请假了,那我们将“请假”的状态为设为1,“工作”的状态为设为0:

personState |= TAKE_OFF;
personState &= ~AT_WORK;//不考虑其他bit,与操作会把~AT_WORK的状态位置为0;

OK,状态的改变很轻松的就能通过对BitMask的操作来实现,同时,我们还需要对状态进行查询的方法。其实查询某个状态,只要用该状态的BitMask去和state做与(&)运算即可。

现在我们查询一下是否在工作状态:

return (personState & AT_WORK )!=0;

再查询一下是否处于回家或者请假状态:

return (personState & (GO_HOME | TAKE_OFF)) !=0;

是不是很简单?每个状态的设置和查询都能非常方便的表示出来,而这仅仅只需要一个bit的内存就可以实现。

BitMask进阶

其实也算不上什么进阶,只是把BitMask的操作做一个归总,用一般的情况来说明。上面已经提到了特定状态位的置0、置1和查询,下面把剩下的一些操作说明一下。

声明:状态集合以S表示,对第j个状态位经行操作(从二进制的低位开始计算,从0开始)

  • 对状态S中的第j个状态位进行翻转操作

S = S^( 1 << j );

  • 获取状态S中,最低一位为1的状态位置

return ( S & (-S) );//负数在计算机中由补码存放

  • 把状态S的末n位置1

S = S|((1<<n)-1);// (1<<n)-1可以形成一个末n位都是1的BitMask

BitMask在Android中的实例

在Android中,像View、ViewGroup等类中,会大量运用到BitMask来经行状态的判断。使用BitMask的好处主要在于节省内存,表示一个状态只要1bit,相当经济划算。对于一个int而言,纯粹使用常量来表示状态,一个int只能表示一种特定的情况,而在BitMask的思想下,则可以表达32种状态的不同组合,并且不同状态之间并不会相互影响。

    public static final int NO_GRAVITY = 0x0000;

    /** Raw bit indicating the gravity for an axis has been specified. */
    public static final int AXIS_SPECIFIED = 0x0001;

    /** Raw bit controlling how the left/top edge is placed. */
    public static final int AXIS_PULL_BEFORE = 0x0002;
    /** Raw bit controlling how the right/bottom edge is placed. */
    public static final int AXIS_PULL_AFTER = 0x0004;
    /** Raw bit controlling whether the right/bottom edge is clipped to its
     * container, based on the gravity direction being applied. */
    public static final int AXIS_CLIP = 0x0008;

    /** Bits defining the horizontal axis. */
    public static final int AXIS_X_SHIFT = 0;
    /** Bits defining the vertical axis. */
    public static final int AXIS_Y_SHIFT = 4;

    /** Push object to the top of its container, not changing its size. */
    public static final int TOP = (AXIS_PULL_BEFORE|AXIS_SPECIFIED)<<AXIS_Y_SHIFT;
    /** Push object to the bottom of its container, not changing its size. */
    public static final int BOTTOM = (AXIS_PULL_AFTER|AXIS_SPECIFIED)<<AXIS_Y_SHIFT;
    /** Push object to the left of its container, not changing its size. */
    public static final int LEFT = (AXIS_PULL_BEFORE|AXIS_SPECIFIED)<<AXIS_X_SHIFT;
    /** Push object to the right of its container, not changing its size. */
    public static final int RIGHT = (AXIS_PULL_AFTER|AXIS_SPECIFIED)<<AXIS_X_SHIFT;

这是类Gravity中的一段代码,看完之前的介绍,应该可以比较好的理解这些BitMask的含义。源码中使用了低四位作为水平重力标志,高四位作为垂直重力标志。每个四位标志中,从低到高,分别表示了四个状态:AXIS_SPECIFIED、AXIS_PULL_BEFORE、AXIS_PULL_AFTER、AXIS_CLIP,对于TOPBOTTOM这种属于垂直状态的标志,将满足条件的组合状态加上了垂直的BitMask移位。

结语:BitMask是一个用来表示状态的好工具,可以非常方便的操作、查询,同时节省内存。在一般的应用中,可能对这点内存的使用并不在意(毕竟一张图片的内存轻轻松松碾压各种int...),但是在Android中,涉及到基础核心的类时,还是需要留意一点。更多在Android源码中出现的BitMask,遇到了再回来看看,就可以更好的理解。

参考文章:

BitMask 使用参考

BITMASKS —— FOR BEGINNERS

本文来自网易实践者社区,经作者周龙授权发布。