话说之前看自定义控件,看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位只有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中,使用一个数值来记录各种状态的集合,使用这个数值的每一位来表达每一种状态。在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的操作做一个归总,用一般的情况来说明。上面已经提到了特定状态位的置0、置1和查询,下面把剩下的一些操作说明一下。
声明:状态集合以S表示,对第j个状态位经行操作(从二进制的低位开始计算,从0开始)
S = S^( 1 << j );
return ( S & (-S) );//负数在计算机中由补码存放
S = S|((1<<n)-1);// (1<<n)-1可以形成一个末n位都是1的BitMask
在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,对于TOP和BOTTOM这种属于垂直状态的标志,将满足条件的组合状态加上了垂直的BitMask移位。
结语:BitMask是一个用来表示状态的好工具,可以非常方便的操作、查询,同时节省内存。在一般的应用中,可能对这点内存的使用并不在意(毕竟一张图片的内存轻轻松松碾压各种int...),但是在Android中,涉及到基础核心的类时,还是需要留意一点。更多在Android源码中出现的BitMask,遇到了再回来看看,就可以更好的理解。
参考文章:
本文来自网易实践者社区,经作者周龙授权发布。