对于 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 这样的核心基础类或者超大型应用,否则,如果连枚举这样内存开销都有问题的话,这个项目的问题就真的大了。

BitMask 使用参考的更多相关文章

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

    平台之大势何人能挡? 带着你的Net飞奔吧!http://www.cnblogs.com/dunitian/p/4822808.html hyper-v安装很多人没弄过,我这里介绍一下.(其他虚拟机参 ...

  2. ifconfig: command not found(CentOS专版,其他的可以参考)

    ifconfig: command not found 查看path配置(echo相当于c中的printf,C#中的Console.WriteLine) echo $PATH 解决方案1:先看看是不是 ...

  3. 参考bootstrap中的popover.js的css画消息弹框

    前段时间小颖的大学同学给小颖发了一张截图,图片类似下面这张图: 小颖当时大概的给她说了下,其实小颖也不知道上面那个三角形怎么画嘻嘻,给她说了DOM结构,具体的css让她自己百度,今天小颖自己参考boo ...

  4. Oracle安装部署,版本升级,应用补丁快速参考

    一.Oracle安装部署 1.1 单机环境 1.2 Oracle RAC环境 1.3 Oracle DataGuard环境 1.4 主机双机 1.5 客户端部署 二.Oracle版本升级 2.1 单机 ...

  5. Angularjs参考框架地址

    1.Table(Grid)参考地址 https://github.com/samu/angular-table https://github.com/daniel-nagy/md-data-table ...

  6. iOS开发之三个Button实现图片无限轮播(参考手机淘宝,Swift版)

    这两天使用Reveal工具查看"手机淘宝"App的UI层次时,发现其图片轮播使用了三个UIButton的复用来实现的图片循环无缝滚动.于是乎就有了今天这篇博客,看到“手机淘宝”这个 ...

  7. SQL 性能调优中可参考的几类Lock Wait

    在我们的系统出现性能问题时,往往避不开调查各种类型 Lock Wait,如Row Lock Wait.Page Lock Wait.Page IO Latch Wait等.从中找出可能的异常等待,为性 ...

  8. RMAN异机恢复快速参考

    应用场景:服务器A为正常运行的生产环境,需要在服务器B上部署一套相同环境做测试. 数据库环境:RHEL6.4 + Oracle 11.2.0.4.7 一. 服务器A备份数据库 1.1 在线备份(数据库 ...

  9. Linux平台oracle 11g单实例 安装部署配置 快速参考

    1.重建主机的Oracle用户 组 统一规范 uid gid 以保证共享存储挂接或其他需求的权限规范 userdel -r oracle groupadd -g 7 oinstall groupadd ...

随机推荐

  1. 利用python2.7正则表达式进行豆瓣电影Top250的网络数据采集及MySQL数据库操作

    转载请注明出处 利用python2.7正则表达式进行豆瓣电影Top250的网络数据采集 1.任务 采集豆瓣电影名称.链接.评分.导演.演员.年份.国家.评论人数.简评等信息 将以上数据存入MySQL数 ...

  2. exec 和 source的区别

    source 就是让 script 在当前 shell 内执行.而不是产生一个 sub-shell 来执行.由exec 也是让 script 在同一个行程上执行,但是原有行程则被结束了. source ...

  3. ubuntu c程序操作系统设备

    最近做一个局域网聊天系统,最后想操作系统播放音频文件.其实,Linux下的声音设备编程比大多数人想象的要简单得多.一般说来,我们常用的声音设备是内部扬声器和声卡,它们都对应/dev目录下的一个或多个设 ...

  4. java 格式化日期(DateFormat)

    import java.text.DateFormat; import java.util.Date; /** * 格式化时间类 DateFormat.FULL = 0 * DateFormat.DE ...

  5. C/C++运算符优先级

    运算符优先级从高至低 优先级 操作符 描述 例子 结合性 1 ()[]->.::++-- 调节优先级的括号操作符数组下标访问操作符通过指向对象的指针访问成员的操作符通过对象本身访问成员的操作符作 ...

  6. ServiceStack.OrmLite 调用存储过程

    最近在做关于ServiceStack.OrmLite调用存储过程时,有问题.发现ServiceStack.OrmLite不能调用存储过程,或者说不能实现我想要的需求.在做分页查询时,我需要传入参数传出 ...

  7. PHP面向对象基础知识总结

    1.类的变量成员叫做“属性”,或者叫“字段”.“特征”,在本文档统一称为“属性”.2.属性中的变量可以初始化,但是初始化的值必须是常数,这里的常数是指php脚本在编译阶段时就为常数,而不是在编译阶段之 ...

  8. 微软职位内部推荐-SDE2 (Windows - Power)

    微软近期Open的职位: SDE2 (Windows - Power) Windows Partner Enablement team in Operating System Group is loo ...

  9. smarty中的母板极制_extends和block标签

    模板继承 继承是从面向对象编程而来的概念,模板继承可以让你定义一个或多个父模板,提供给子模板来进行扩展. 扩展继承意味着子模板可以覆盖部分或全部父模板的块区域. 继承结构可以是多层次的,所以你可以继承 ...

  10. Linux命令zip和unzip

    问题描述:        使用Linux中命令zip和unzip 问题解决: 命令名: zip  功能说明:压缩文件. 语 法:zip [-AcdDfFghjJKlLmoqrSTuvVwXyz$][- ...