Android系统中Flag的位操作设计

Android系统中Flag的位设计

在android的系统类中经常看到精妙的位设计,从而取代了繁多的boolean类型,结构紧凑

例如View中的flag

* |-------|-------|-------|-------|
 *                                 1 PFLAG_WANTS_FOCUS
 *                                1  PFLAG_FOCUSED
 *                               1   PFLAG_SELECTED
 *                              1    PFLAG_IS_ROOT_NAMESPACE
 *                             1     PFLAG_HAS_BOUNDS
 *                            1      PFLAG_DRAWN
 *                           1       PFLAG_DRAW_ANIMATION
 *                          1        PFLAG_SKIP_DRAW
 *                        1          PFLAG_REQUEST_TRANSPARENT_REGIONS
 *                       1           PFLAG_DRAWABLE_STATE_DIRTY
 *                      1            PFLAG_MEASURED_DIMENSION_SET
 *                     1             PFLAG_FORCE_LAYOUT
 *                    1              PFLAG_LAYOUT_REQUIRED
 *                   1               PFLAG_PRESSED
 *                  1                PFLAG_DRAWING_CACHE_VALID
 *                 1                 PFLAG_ANIMATION_STARTED
 *                1                  PFLAG_SAVE_STATE_CALLED
 *               1                   PFLAG_ALPHA_SET
 *              1                    PFLAG_SCROLL_CONTAINER
 *             1                     PFLAG_SCROLL_CONTAINER_ADDED
 *            1                      PFLAG_DIRTY
 *            1                      PFLAG_DIRTY_MASK
 *          1                        PFLAG_OPAQUE_BACKGROUND
 *         1                         PFLAG_OPAQUE_SCROLLBARS
 *         11                        PFLAG_OPAQUE_MASK
 *        1                          PFLAG_PREPRESSED
 *       1                           PFLAG_CANCEL_NEXT_UP_EVENT
 *      1                            PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH
 *     1                             PFLAG_HOVERED
 *    1                              PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK
 *   1                               PFLAG_ACTIVATED
 *  1                                PFLAG_INVALIDATED
 * |-------|-------|-------|-------|
 */
/** {@hide} */
static final int PFLAG_WANTS_FOCUS                 = 0x00000001;
/** {@hide} */
static final int PFLAG_FOCUSED                     = 0x00000002;
/** {@hide} */
static final int PFLAG_SELECTED                    = 0x00000004;
/** {@hide} */
static final int PFLAG_IS_ROOT_NAMESPACE           = 0x00000008;
/** {@hide} */
static final int PFLAG_HAS_BOUNDS                  = 0x00000010;
/** {@hide} */
static final int PFLAG_DRAWN                       = 0x00000020;
/**
 * When this flag is set, this view is running an animation on behalf of its
 * children and should therefore not cancel invalidate requests, even if they
 * lie outside of this view's bounds.
 *
 * {@hide}
 */
static final int PFLAG_DRAW_ANIMATION              = 0x00000040;
/** {@hide} */
static final int PFLAG_SKIP_DRAW                   = 0x00000080;
/** {@hide} */
static final int PFLAG_REQUEST_TRANSPARENT_REGIONS = 0x00000200;
/** {@hide} */
static final int PFLAG_DRAWABLE_STATE_DIRTY        = 0x00000400;
/** {@hide} */
static final int PFLAG_MEASURED_DIMENSION_SET      = 0x00000800;
/** {@hide} */
static final int PFLAG_FORCE_LAYOUT                = 0x00001000;
/** {@hide} */
static final int PFLAG_LAYOUT_REQUIRED             = 0x00002000;

private static final int PFLAG_PRESSED             = 0x00004000;

/** {@hide} */
static final int PFLAG_DRAWING_CACHE_VALID         = 0x00008000;
/**
 * Flag used to indicate that this view should be drawn once more (and only once
 * more) after its animation has completed.
 * {@hide}
 */
static final int PFLAG_ANIMATION_STARTED           = 0x00010000;

private static final int PFLAG_SAVE_STATE_CALLED   = 0x00020000;

/**
 * Indicates that the View returned true when onSetAlpha() was called and that
 * the alpha must be restored.
 * {@hide}
 */
static final int PFLAG_ALPHA_SET                   = 0x00040000;

/**
 * Set by {@link #setScrollContainer(boolean)}.
 */
static final int PFLAG_SCROLL_CONTAINER            = 0x00080000;

/**
 * Set by {@link #setScrollContainer(boolean)}.
 */
static final int PFLAG_SCROLL_CONTAINER_ADDED      = 0x00100000;

/**
 * View flag indicating whether this view was invalidated (fully or partially.)
 *
 * @hide
 */
static final int PFLAG_DIRTY                       = 0x00200000;

/**
 * Mask for {@link #PFLAG_DIRTY}.
 *
 * @hide
 */
static final int PFLAG_DIRTY_MASK                  = 0x00200000;

今天我们就看下这是如何实现的

看下面的测试代码

int a1=0x00000001;
int a2=0x00000002;
int a3=0x00000004;
int a4=0x00000008;
int a5=0x00000010;
int a6=0x00000020;
int a7=0x00000040;
int a8=0x00000080;
int a9=0x00000100;

int flag_mask=0;

Log.i("test","result1:"+Integer.toBinaryString(a1));
Log.i("test","result2:"+Integer.toBinaryString(a2));
Log.i("test","result3:"+Integer.toBinaryString(a3));
Log.i("test","result4:"+Integer.toBinaryString(a4));
Log.i("test","result5:"+Integer.toBinaryString(a5));
Log.i("test","result6:"+Integer.toBinaryString(a6));
Log.i("test","result7:"+Integer.toBinaryString(a7));
Log.i("test","result8:"+Integer.toBinaryString(a8));
Log.i("test","result9:"+Integer.toBinaryString(a9));

结果:
2021-11-03 11:27:46.635 29885-29885/com.xx.bb I/test: result1:1
2021-11-03 11:27:46.635 29885-29885/com.xx.bb I/test: result2:10
2021-11-03 11:27:46.635 29885-29885/com.xx.bb I/test: result3:100
2021-11-03 11:27:46.635 29885-29885/com.xx.bb I/test: result4:1000
2021-11-03 11:27:46.635 29885-29885/com.xx.bb I/test: result5:10000
2021-11-03 11:27:46.635 29885-29885/com.xx.bb I/test: result6:100000
2021-11-03 11:27:46.635 29885-29885/com.xx.bb I/test: result7:1000000
2021-11-03 11:27:46.635 29885-29885/com.xx.bb I/test: result8:10000000
2021-11-03 11:27:46.635 29885-29885/com.xx.bb I/test: result9:100000000 

上面这个代码是模仿了系统中的代码写法实现的,a1~a9是各种标志,这些标志代表各种功能的true,false

我们看到这些标志有个规律是按照:1,2,4,8,10,20,40,80,100 因为这个是16进制的(0x代表16进制)所以意思是数字大小是前面个的一倍计算的,这样可以保证转成2进制后,除了自己标志位那里是1,其余都是0

1、添加标志

我们经常要往flag_mask(各种标志的结果集合)里写入这个标志,该如何操作的,答案是应该用| 或位运算符

例如:

int flag_mask=0;//当前是空的 ,什么标志都没有
int a1=0x00000001;
int a2=0x00000002;
int a3=0x00000004;
int a4=0x00000008;
往里写入一个a1,a4,这两个标志:
flag_mask = flag_mask|a1;
flag_mask = flag_mask|a4;
或则直接简写
flag_mask |= a1;
flag_mask |= a4;

Log.i("test","添加标志:"+Integer.toBinaryString(flag_mask));

结果:
2021-11-03 11:42:27.785 31601-31601/com.xx.bb I/test: 添加标志:1001
这样成在1,4位分别添加了标志位
分析:
    flag_mask=00000000 00000000 00000000 00000000  int是4字节,32位的
    a1       =00000000 00000000 00000000 00000001 
    按位或操作的结果:
    result   =00000000 00000000 00000000 00000001
    所以通过|操作符可以实现标志位的添加功能
    


2、提取标志

 //提取标志
//提取的功能主要是用来判断某个标志位是否存在,是否已经设置了
boolean isA1Seted=(flag_mask&a1) == a1;
Log.i("test","a1是否设置:"+isA1Seted);

boolean isA4Seted=(flag_mask&a4) == a4;
Log.i("test","a4是否设置:"+isA4Seted);


运行结果:
2021-11-03 11:48:24.304 32664-32664/com.xx.bb I/test: a1是否设置:true
2021-11-03 11:48:24.304 32664-32664/com.xx.bb I/test: a4是否设置:true

分析:
    flag_mask=00000000 00000000 00000000 00001001  int是4字节,32位的
    a1       =00000000 00000000 00000000 00000001 
    a4       =00000000 00000000 00000000 00001000 
    通过按位于操作符& 
    resultA1 =00000000 00000000 00000000 00000001
    resultA4 =00000000 00000000 00000000 00001000
    所以要提取某个标志位,然后判断是否存在就使用&操作符
    
    

3、删除标志

//删除标志
//有的时候我们会把某个标志从mask里面删除,该怎么操作呢?
flag_mask &= ~a1;
flag_mask &= ~a4;
运行结果:
2021-11-03 11:58:59.481 1833-1833/com.xx.bb I/test: 删除后的结果flag_mask:0
    
Log.i("test","删除后的结果flag_mask:"+Integer.toBinaryString(flag_mask));
分析:
    flag_mask=00000000 00000000 00000000 00001001  int是4字节,32位的
    ~a1      =11111111 11111111 11111111 11111110  按位取反的结果
    ~a4      =11111111 11111111 11111111 11110111  按位取反的结果
    按位与&
    flag_mask=00000000 00000000 00000000 00001000
    flag_mask=00000000 00000000 00000000 00000000
    最终结果把a1和a4都删除了
    
    

总结:系统的源代码是很好的老师,我们写代码的时候如果有很多的boolean变量逻辑,我们就可以按照这样通过位操作实现标志的方式来实现功能,这样代码高效紧凑

猜你喜欢

转载自blog.csdn.net/fagawee/article/details/121118296