首先说一下基本位运算知识:
位运算:位运算就是把数字用二进制表示之后,对每一位上0或者1的运算。
位运算总共只有5种运算:与、或、异或、左移、右移。如下表:
左移运算:
左移运算 左移n位的时候,最左边的n位将被丢弃,同时在最右边补上n个0.比如:
00001010 << 2 = 00101000
10001010 << 3 = 01010000
右移运算:
右移运算符m>>n表示把m右移n位。右移n位的时候,最右边的n位将被丢弃。但右移时处理最左边位的情形要稍微复杂一点。这里要特别注意,如果数字是一个无符号数值,则用0填补最左边的n位。如果数字是一个有符号数值,则用数字的符号位填补最左边的n位。也就是说如果数字原先是一个正数,则右移之后再最左边补n个0;如果数字原先是负数,则右移之后在最左边补n个1.下面是堆两个8位有符号数作右移的例子:
00001010 >> 2 = 00000010
10001010 >> 3 = 11110001
关于移位的运算有这样的等价关系:
把整数右移一位和把整数除以2
a << = n ; //a左移一位等效于a = a * 2^n;
进制之间转换:
主要是2进制 8进制 16进制 之间的转换
16进制转换2进制 : 16进制每一位 对应 二进制中的四位
0xF4 ——– 1111 0100
8进制转换2进制 : 8进制每一位 对应二进制中的三位
364 ———011 110 100
10进制转换2进制:
这个我就不多说了一张图就明白
那么10 转换为2进制 从下向上读就是01010
位运算在Android中的使用场景
之前博客在介绍MeasureSpec和TypeValue的时候都一定程度说到到位运算,如果还不知道的话,可参考:
http://blog.csdn.net/wning1/article/details/64497446
http://blog.csdn.net/wning1/article/details/64137354
拿MesureSpec来说吧!子View需要复写onMeasure(),对于父布局推荐的大小和模式,可以通过MeasureSpec的getMode()和getSize()方法去获取,想象一下如果不使用位运算的这种形式,我们还可以采用那种方式?定义一个类对象包括int类型size 和int类型mode 两个参数也行。那么为啥要用位运算来计算呢?答案:节省内存。4个字节就能干的事情何必要8个字节去做呢
这几天我在Android源码又看到了一个位运算相关代码:
Fragment 如果要跳转到一个Activity并对Activity返回进行数据处理那么我们会选择调用startActivityForResult方法,以下为部分代码(compileSdkVersion 25):
Fragment 类
public void startActivityForResult(Intent intent, int requestCode) {
startActivityForResult(intent, requestCode, null);
}
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");//mHost即为Fragment绑定的Activity对象
}
mHost.onStartActivityFromFragment(this /*fragment*/, intent, requestCode, options);//调用FragmentActivity中的onStartActivityFromFragment
}
FragmentActivity类
@Override
public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode) {
FragmentActivity.this.startActivityFromFragment(fragment, intent, requestCode);
}
public void startActivityFromFragment(Fragment fragment, Intent intent,
int requestCode) {
startActivityFromFragment(fragment, intent, requestCode, null);
}
public void startActivityFromFragment(Fragment fragment, Intent intent,
int requestCode, @Nullable Bundle options) {
mStartedActivityFromFragment = true;
try {
if (requestCode == -1) {
ActivityCompat.startActivityForResult(this, intent, -1, options); //默认startActivity 传递的requestCode == -1
return;
}
checkForValidRequestCode(requestCode);
int requestIndex = allocateRequestIndex(fragment);
ActivityCompat.startActivityForResult(
this, intent, ((requestIndex + 1) << 16) + (requestCode & 0xffff), options);
} finally {
mStartedActivityFromFragment = false;
}
}
static void checkForValidRequestCode(int requestCode) {
if ((requestCode & 0xffff0000) != 0) {
throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
mFragments.noteStateNotSaved();
int requestIndex = requestCode>>16;
if (requestIndex != 0) {
requestIndex--;
String who = mPendingFragmentActivityResults.get(requestIndex);
mPendingFragmentActivityResults.remove(requestIndex);
if (who == null) {
Log.w(TAG, "Activity result delivered for unknown Fragment.");
return;
}
Fragment targetFragment = mFragments.findFragmentByWho(who);
if (targetFragment == null) {
Log.w(TAG, "Activity result no fragment exists for who: " + who);
} else {
targetFragment.onActivityResult(requestCode & 0xffff, resultCode, data);
}
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
可以看到Fragment中调用startActivityForResult最终会调用到FragmentActivity中的startActivityFromFragment,如果requestCode!= -1 则调用checkForValidRequestCode进行code 数值判断,requestCode为int类型,requestCode & 0x ffff0000 如果不为0则说明requestCode高16位上至少1位为1也就是说数值肯定已经超过了65535。则会抛出参数非法异常,那么为什么不让用高16位呢?我们接向下看。 ((requestIndex + 1) << 16) + (requestCode & 0xffff) 重新做为了requestCode值,requestIndex + 1 向做移动了16位然后和旧的requetCode合并,那么现在新的requestCode的低16上存储的是真实的requestCode值,而高16则是存储的fragment的index,怪不得不让我们使用高16位呢。
在onActivityResult方法可以看到requestCode向右移动16位还原fragment的index,求出fragment如果fragment!=null则调用fragment的onActivityResult方法,否则回调给Activity。
ok,到这fragment这部分源码就已经分析完毕了,相信对位运算有了更加深刻的理解。
项目运用
以下为项目中蓝牙连接状态相关代码:
/** 处于已断开状态时 */
public static final int UNCONNECT = 1;
/** 处于扫描状态时 */
public static final int SEARCHING = 1 << 1;
/** 处于正在连接时 */
public static final int CONNECTING = 1 << 2;
/** 处于已连接闲置状态时 */
public static final int IDLE = 1 << 3;
/** 处于正在断开状态时 */
public static final int DISCONNECTING = 1 << 4;
/** 处于工作状态时 */
public static final int WORKING = 1 << 5;
/** 处于闲置,或者工作状态集时 */
public static final int MASK_CONNECTED = IDLE | WORKING;
/** 处于扫描,或者连接,或者正在断开状态集时 */
public static final int MASK_BUSY_CONNECTING = SEARCHING | CONNECTING | DISCONNECTING;
我如果要想判断设备是否处于连接状态只需要 state & MASK_CONNECTED != 0,是否处于连接状态只需要state & MASK_CONNECTING != 0 即可。
想象一下传统的做法应该是所有的状态都定义成int类型,如果要判断是否处于连接状态,我们需要这样做 state == SEARCHING || state ==CONNECTING || state == DISCONNECTING .
孰优孰劣,一目了然,ok,关于位运算今天就记录到这。