BitMask 使用参考

对于 Java 类应用,内存方面需要注意:

  1. 不要占用大量内存,否则可用内存少;触发 GC 或 OutOfMemoryError
  2. 不要频繁创建对象,频繁内存分配,触发 GC。

对于枚举和常量:

  1. 使用枚举,并不会使得对象的创建更加频繁。
  2. 枚举类会比常量占用更多的内存,在程序运行期间,如果不卸载枚举类,内存就一直占用着。

    相对于常量,枚举占用的内存是较为可观的。

使用常量,可以大量节省内存,在 C 之类的语言中,大量使用 BitMask 来进行状态表示。

在 Android 中,也大量地使用了 BitMask,比如 android.view.View 这个类。

位操作

在使用 BitMask 前,我们先复习一下基本的位操作。

  1. NOT

    NOT 0000 0001
      = 1111 1110
    

    比如:

    int a = 1;
    int b = ~a;
    
  2. OR
    OR  0000 0001
        0000 0010
      = 0000 0011
    

    比如:

    int a = 1;
    int b = 2;
    int c = a | b;
    
  3. AND
    AND 0000 0101
        0000 0110
      = 0000 0100
    

    比如:

    int a = 5;
    int b = 6;
    int c = a & b;
    

BitMask

我们知道,每一个 bit 可以有两种取值:0 或 1。

BitMask 采用一个数值来记录状态,使用这个数值的每一位来表达一个状态。

使用 BitMask 可用非常少的资源表达非常丰富的状态。

在 Java 中,一个 byte 类型,有 8 位(bit),可以表达 8 个不同的状态,并且这些状态是互不影响的。而 int 类型,则有 32 位,可以表达 32 种状态。

更为重要的是,基于 BitMask 可 非常简单地 进行组合状态查询。

BitMask 基本操作

假设我们用一个表示状态的数值: status,初始值为 0。

byte status = 0;

我们定义一个 mask 数值,该数第二位为 1:0000 0010

我们把 1 往左移动 1 位来得到这个数:

byte mask = 0x01 << 1;
  1. 设置状态

    其他位不管,把第 2 位变为 1 即可。

        xxxx xxxx
    OR  0000 0010
      = xxxx xx1x
    

    代码

    status |= mask;
    
  2. 清除状态

    其他位不管,把第 2 位置为 0。

        xxxx xxxx
    AND 1111 1101
      = xxxx xx0x
    

    这实际是对 status 和 mask 的反码进行逻辑『与』运算:

    status &= ~mask;
    
  3. 查询状态

    确定第 2 位是 0 还是 1,和 mask 进行逻辑『与』运算:

        xxxx xxxx
    AND 0000 0010
      = 0000 00x0
    

    如果为 1,返回一个大于 0 的值,否则返回 0。

    boolean isOn = (status & mask) > 0;
    

例子

下面结合一个例子来做说明。

相关代码在这里: https://github.com/liaohuqiu/android-BitMaskSample

李白 是个诗人,生活简单『朴素』:有时候写诗;有时候喝酒;有时候边写诗,边喝酒。

不管是忙于写诗还是忙于喝酒,李白都是在忙碌状态中。

我们用一个字节来表示他的状态,一个字节有 8 位,我们从低位起开始取两位分别代表写诗和喝酒。

   writing  ------+
                  |
                  v
     -------+---+---+---+
       x  x |   |   | x |
     -------+---+---+---+
              ^
              |
   drinking --+

两个 mask 为:

// 0000 0010
private static final byte STATE_BUSY_IN_WRITING = 0x01 << 1;

// 0000 0100
private static final byte STATE_BUSY_IN_DRINKING = 0x01 << 2;
  1. 状态设置与清除

    以设置 drinking 状态为例子,设置状态即和 mask 值进行逻辑『或』,清除状态与 mask 的反码进行逻辑『与』运算。

    public void setBusyInDrinking(boolean busy) {
        if (busy) {
            mState |= STATE_BUSY_IN_DRINKING;
        } else {
            mState &= ~STATE_BUSY_IN_DRINKING;
        }
    }
    
  2. 状态查询

    与 mask 进行逻辑『与』运行,判断是否为零即可:

    public boolean isBusyInDrinking() {
        return (mState & STATE_BUSY_IN_DRINKING) != 0;
    }
    
  3. 组合状态查询

    不管是忙于写诗还是忙于饮酒,都称为『李白很忙』,这是一种组合状态。只要处于这两种状态中的一种,即处于组合状态中。

    要进行状态组合,用逻辑『或』运算即可,当进行多个状态组合时,特别方便:

    STATE_BUSY_MASK = STATE_BUSY_IN_WRITING | STATE_BUSY_IN_DRINKING
    

    判断是否处于组合状态中:

    public boolean isBusy() {
        return (mState & STATE_BUSY_MASK) != 0;
    }
    

Android 中的 IntDef

使用 IntDef 注解来声明常量值,定义变量时,加上 IntDef 所定义的声明,编译器会检查赋值是否合法。

声明:

// 最后 8 位 0000 1100
static final int VISIBILITY_MASK = 0x0000000C;

public static final int VISIBLE = 0x00000000;

// 最后 8 位 0000 0100
public static final int INVISIBLE = 0x00000004;

// 最后 8 位 0000 1000
public static final int GONE = 0x00000008;

@IntDef({VISIBLE, INVISIBLE, GONE})
@Retention(RetentionPolicy.SOURCE)
public @interface Visibility {}

使用:

public void setVisibility(@Visibility int visibility) {
    setFlags(visibility, VISIBILITY_MASK);
}

上面我们看到,代码中采用了最左的 3,4 位来表达 View 的可见性。

结论

除了 IntDef,还有 StringDef,有兴趣的同学可以看源码。

在 Android 的代码中有大量的 BitMask 的运用,像 ViewMotionEvent 这样的核心基础类中,需要认真考虑内存的使用,能省则省。

如果你真想完全地掌控内存的使用,追求卓越的品质,想最大限度节省内存,BitMask 是你不错的选择。

同时,我们也应该清楚枚举也不是不能用。

我听到过很多论调,说用『枚举不好,官方也建议别用,因为占用很多内存,效率不高』,这些也都是人云亦云的典型。

实际上,除非你写的是类似 View 这样的核心基础类或者超大型应用,否则,如果连枚举这样内存开销都有问题的话,这个项目的问题就真的大了。

时间: 2024-10-08 03:07:39

BitMask 使用参考的相关文章

【资源共享】《Camera_for_RockChipSDK参考说明_v4.1》下载

关于摄像头调试的文档<Camera_for_RockChipSDK参考说明_v4.1> 下载地址:http://developer.t-firefly.com/thread-12429-1-1.html

angular参考手册拷贝

AngularJS 参考手册 AngularJS 指令 本教程用到的 AngularJS 指令 : 指令 描述 ng-app 定义应用程序的根元素. ng-bind 绑定 HTML 元素到应用程序数据 ng-bind-html 绑定 HTML 元素的 innerHTML 到应用程序数据,并移除 HTML 字符串中危险字符 ng-bind-template 规定要使用模板替换的文本内容 ng-blur 规定 blur 事件的行为 ng-change 规定在内容改变时要执行的表达式 ng-check

分析实现zc706 AMP-linux和裸奔的注意点更新-参考xapp1078 latest info.

在上一篇博文中,研究了xapp1078,它的目标平台是zc702.本文分析在zc706实现AMP时需要修改的关键点.参考资料:http://www.wiki.xilinx.com/XAPP1078+Latest+Information 1,zc706中的bootROM中,采用了V2版本.该版本使用了不同的WFE loop,CPU1被唤醒(中断.sev或者其他事件)后,会读取0xFFFFFFF0位置的数据与0xFFFFFF2C比较,如果与0xFFFFFF2C相同,则回到WFE状态(待机).如果与0

.net 自己写的操作Excel 导入导出 类(以供大家参考和自己查阅)

由于现在网页很多都关系到Excel 的操作问题,其中数据的导入导出更是频繁,作为一个菜鸟,收集网上零散的知识,自己整合,写了一个Excel导入到GridView ,以及将GridView的数据导出到EXCEL的类方法,以供参考和方便自己以后查阅. 1 #region 引用部分 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Web; 6 using System.Dat

排序算法--概述和参考

1 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 我们这里说说八大排序就是内部排序. 当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序.堆排序或归并排序序. 快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短: 2 参考 http://blog.csdn.net/hguisu/article/details/77

Hyper-v 安装CentOS 7 (其他虚拟机一样参考)

平台之大势何人能挡? 带着你的Net飞奔吧!http://www.cnblogs.com/dunitian/p/4822808.html hyper-v安装很多人没弄过,我这里介绍一下.(其他虚拟机参考==> 点我吧) 右击,新建虚拟机 #通用部分开始 收工~~~ Linux 开机时网络自动连接:http://www.cnblogs.com/dunitian/p/4975830.html ifconfig: command not found(CentOS专版,其他的可以参考)http://ww

MVC 构建图片/文件选择器 参考其它CMS功能

实现结果,如下 点击选择图片,弹出一个iframe框 顶部默认图片根目录,依次下面是文件列表 底部是选择的文件地址,以及上传新的图片和文件 加载iframe 调用js方法 function initFiles(path) { if (path == spath) { $("#fback").addClass("hidden"); } else { $("#fback").removeClass("hidden"); } var

Docker run 参考指南

Docker run参考指南 docker运行在一个独立的隔离的进程中. 当用户执行dockerrun,它将启动一个有着独立的文件系统,独立的网络和独立的进程树的进程. 基本的docker run命令的格式: docker run  [OPTIONS] IMAGE[:TAG] [COMMAND] [ARG...] [OPTIONS]分为两种: 1.对于用户独占性的设置: 前台.后台运行 容器定义 网络设定 容器在CPU和内存中的运行时间 运行权限和LXC配置 2.在操作者和开发者之间的共享设定,

EL表达式参考手册

一.EL简介 1.语法结构     ${expression}2.[]与.运算符     EL 提供.和[]两种运算符来存取数据.    当要存取的属性名称中包含一些特殊字符,如.或?等并非字母或数字的符号,就一定要使用 [].例如:        ${user.My-Name}应当改为${user["My-Name"] }    如果要动态取值时,就可以用[]来做,而.无法做到动态取值.例如:        ${sessionScope.user[data]}中data 是一个变量3