位运算在Android中的应用
Posted Jadyli1
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了位运算在Android中的应用相关的知识,希望对你有一定的参考价值。
文章目录
摘要
本文主要讲解了位运算在android中的简单使用,配合Android源码的分析,达到深入理解并熟练运用。
什么是位运算?
- 与: 2(0010) & 1(0001) => 0(0000)
- 或: 2(0010) | 1(0001) => 3(0011)
- 非: ~2(0010) => 13(1101)
- 异或: 2(0010) ^ 1(0001) => 3(0011)
0
和另一个数相与得到0
,1
与另一个数相与则另一个数维持不变,这点很重要!说明0可以清空一个数,1可以保留一个数。异或是去同存异,只有两个bit位不一样结果才会为1。
负数的表示,通常都是用补码表示的,比如-2,原码为0010,反码为1101,反码+1得到补码为1110。当然这样算太麻烦了,我大学老师教过一个简单的方法,原码0010从右往左看,以第一次出现的1为界,可以分成两份(左边00
和右边0
),左边全部取反,右边保持不变,即1110,嗯哼…是不是跟之前的补码一样。。。。
在MD5中的应用
public static String md5(String content)
try
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.reset();
digest.update(content.getBytes(Charset.forName("UTF-8")));
byte[] hash = digest.digest();
StringBuilder hexString = new StringBuilder();
for (byte b : hash)
int number = b & 0xff;
if (number < 0x10)
hexString.append('0');
hexString.append(Integer.toHexString(number));
return hexString.toString();
catch (NoSuchAlgorithmException e)
return "000000000000000000000000000000000000";
这是一段典型的MD5加密代码。这里有两个地方需要注意,一个是
if (number < 0x10)
hexString.append('0');
这里要手动判断加0
,否则最后得出的结果可能不足32位,如果用来加密密码什么的,可能会导致和其他平台计算md5得出的结果不一致。
另一个要注意的的是
int number = b & 0xff;
有人可能会问了,& 0xff
干嘛用的?o(*≧▽≦)ツ这里要长篇大论了。。。
byte
有8
位,int
有32
位,如果要转换的byte
是个负数,比如-2
,二进制补码表示为1111 1110
。
直接转换为int
后,高24位进行补1
(窄的整型转换成较宽的整型时符号扩展规则:如果最初的数值类型是有符号的,那么就执行符号扩展(即如果符号位为1,则扩展为1,如果为零,则扩展为0)),得到**11111111 11111111 11111111
** 1111 1110
。此时二进制转化为十进制后的值是正确的(还是-2
),但是二进制数据已经错了,因为它比原始的二进制数据多了24个1
。
执行& 0xff
操作以后,高24位全部会变成0
,即**00000000 00000000 00000000
** 1111 1110
,起到了只保留低8位的作用。这时候二进制数据是跟原来一样的,转化成十进制是错的(不是-2
了)。
当然十六进制也是一样的,-2
(0xfe) 直接转 int
得到 0xfffffffe
,-2 & 0xff => 0x000000fe。
所以个人理解,执行& 0xff
操作是为了在非十进制计算的场景下保持byte数据转化前后的二进制值是一致的(当然这样也保持了十六进制数据的一致性)。
上面代码中的这个&
操作正是出于保持byte值转换前后数据一致的原因。
在Android源码中的应用
举个栗子,我要在对象里面保存一个flag变量,这个变量的表示该对象是否支持某一种或多种属性,来看下如何定义这些属性,如何给flag变量添加某种属性,如何判断flag变量是否支持某种属性。
- 首先,我们可以使用16进制的方式来定义几个属性。比如可点击和长按属性。
public static final int CLICKABLE = 0x01;
public static final int LONG_CLICKABLE = 0x02;
private int mFlags;
为什么是0x01和0x02?先往下看。
- 设置flag
public void setFlag(int flag)
mFlags |= flag;
假设现在mFlags
为0,调用setFlag(CLICKABLE)
:
mFlags | CLICKABLE => 0000 | 0001 => 0001
现在mFlags的值是0001,继续调用setFlag(LONG_CLICKABLE)
:
mFlags | LONG_CLICKABLE => 0001 | 0010 => 0011
现在mFlags的值是0011。
- 获取flag
/**
*
* @param flag @link #CLICKABLE#LONG_CLICKABLE
*/
public boolean isSupport(int flag)
return mFlags & flag;
调用isSupport(CLICKABLE)判断是否支持点击:
mFlags & CLICKABLE => 0x11 & 0x01 => 0x01,即1 => 返回true
通过或操作来设置属性,通过与操作来获取属性,这两个操作的关键点是只操作对应的标志位,比如点击只操作第一位,长按只操作第二位。这里我们知道了&
操作可以取出标志位,|
操作可以添加标志位。
下面来看下Android
的源码是怎么用的,我们经常设置View
的Visibility
属性,它其实就是按上述方式设置的,只不过复杂点。这个属性保存在View
的mViewFlags
变量中。
/**
* The view flags hold various views states.
* @hide
*/
@ViewDebug.ExportedProperty(formatToHexString = true)
int mViewFlags;
这个变量保存了很多View的状态属性。
/** @hide */
@IntDef(VISIBLE, INVISIBLE, GONE)
@Retention(RetentionPolicy.SOURCE)
public @interface Visibility
/**
* This view is visible.
* Use with @link #setVisibility and <a href="#attr_android:visibility">@code
* android:visibility.
*/
public static final int VISIBLE = 0x00000000;
/**
* This view is invisible, but it still takes up space for layout purposes.
* Use with @link #setVisibility and <a href="#attr_android:visibility">@code
* android:visibility.
*/
public static final int INVISIBLE = 0x00000004;
/**
* This view is invisible, and it doesn't take any space for layout
* purposes. Use with @link #setVisibility and <a href="#attr_android:visibility">
* @code android:visibility.
*/
public static final int GONE = 0x00000008;
/**
* Mask for use with setFlags indicating bits used for visibility.
* @hide
*/
static final int VISIBILITY_MASK = 0x0000000C;
private static final int[] VISIBILITY_FLAGS = VISIBLE, INVISIBLE, GONE;
首先定义了一个Visibility
注解,这个注解加在变量上可以约束变量的值为@IntDef
中声明的其中一个,如果传入该变量的值在@IntDef
中没有声明,则会有红色警告,但是这是源码警告(@Retention(RetentionPolicy.SOURCE)),不会影响编译。
接下来看下属性常量,可以发现都是16进制,而且貌似结尾还有一定的规律,0,4,8,这样看看不出什么,转成二进制,我们只观察低8位:
VISIBLE(0): 0000 0000
INVISIBLE(4): 0000 0100
GONE(8): 0000 1000
还是跟刚才的小例子一样,每个属性单独占一位!
还剩下一个VISIBILITY_MASK
,这个常量叫做位掩码,功能是屏蔽掉不相关的位。
VISIBILITY_MASK©: 0000 1100
这个掩码的意思是只保留第三位和第四位,通过掩码我们可以确定,二进制第三位和第四位是用来表示Visibility属性的。
接着来看如何给mViewFlags
赋值。
/**
* Set the visibility state of this view.
*
* @param visibility One of @link #VISIBLE, @link #INVISIBLE, or @link #GONE.
* @attr ref android.R.styleable#View_visibility
*/
@RemotableViewMethod
public void setVisibility(@Visibility int visibility)
setFlags(visibility, VISIBILITY_MASK);
这个setFlags(int flags, int mask)
方法很长,我们假设当前的mViewFlags
为1101 0100
,传入的参数为VISIBLE
、VISIBILITY_MASK
,挑重点一点一点来分析。
int old = mViewFlags;
mViewFlags = (mViewFlags & ~mask) | (flags & mask);
首先把当前的mViewFlags
保存一份,然后再赋值。分解这个赋值的过程如下:
~mask => ~VISIBILITY_MASK => ~0000 1100 => 1111 0011
位掩码取反,发现第三四位为0,可以预测它是想要清除第三、四位,其他位为1是要保留。
mViewFlags & ~mask => 1101 0100 & 1111 0011 => 1101 0000
第三、四位清空了,其他标志位没变,此时我们可以得出结论:&~
操作可以清除标志位。
flags & mask => VISIBLE & VISIBILITY_MASK => 0000 0000 & 0000 1100 => 0000 0000
位掩码清掉了flags的其他标志位(虽然其他标志位本来就是0),只保留第三、四位(虽然第三、四位本来就是0 (≧▽≦)/)。
mViewFlags = (mViewFlags & ~mask) | (flags & mask) => 1101 0000 | 0000 0000 => 1101 0000
原来的mViewFlags
的值是1101 0100
,传进来的flags
是0000 0000
,最后得出的值是1101 0000
,说明属性设置好了。
继续往下看:
int changed = mViewFlags ^ old;
if (changed == 0)
return;
这里有一个异或操作,检查变量有没有发生变化,没变化就返回。
final int newVisibility = flags & VISIBILITY_MASK;
if (newVisibility == VISIBLE)
if ((changed & VISIBILITY_MASK) != 0)
/*
* If this view is becoming visible, invalidate it in case it changed while
* it was not visible. Marking it drawn ensures that the invalidation will
* go through.
*/
mPrivateFlags |= PFLAG_DRAWN;
invalidate(true);
needGlobalAttributesUpdate(true);
// a view becoming visible is worth notifying the parent
// about in case nothing has focus. even if this specific view
// isn't focusable, it may contain something that is, so let
// the root view try to give this focus if nothing else does.
if ((mParent != null) && (mBottom > mTop) && (mRight > mLeft))
mParent.focusableViewAvailable(this);
首先flags & VISIBILITY_MASK
只保留Visibility
的标志位,然后判断标志位是否是VISIBLE
,如果是,再判断改变的二进制位是否是Visibility
的标识位,如果是,然后就执行刷新操作等等。
/* Check if the GONE bit has changed */
if ((changed & GONE) != 0)
needGlobalAttributesUpdate(false);
requestLayout();
if (((mViewFlags & VISIBILITY_MASK) == GONE))
if (hasFocus()) clearFocus();
clearAccessibilityFocus();
destroyDrawingCache();
if (mParent instanceof View)
// GONE views noop invalidation, so invalidate the parent
((View) mParent).invalidate(true);
// Mark the view drawn to ensure that it gets invalidated properly the next
// time it is visible and gets invalidated
mPrivateFlags |= PFLAG_DRAWN;
if (mAttachInfo != null)
mAttachInfo.mViewVisibilityChanged = true;
判断代表GONE(0x00000008: 0000 1000)
的第四位标志位是不是改变了,然后执行刷新操作,接着判断GONE
属性是设置了还是清除了,并执行相应的操作。
看到这里,相信其余代码的位运算你都能理解了,有兴趣的可以自己看下面贴出的setFlags()方法的完整代码,只要一步一步分解,其实Android源码没你想的那么难( ̄▽ ̄)",那本文就到此结束啦O(∩_∩)O
/**
* Set flags controlling behavior of this view.
*
* @param flags Constant indicating the value which should be set
* @param mask Constant indicating the bit range that should be changed
*/
void setFlags(int flags, int mask)
final boolean accessibilityEnabled =
AccessibilityManager.getInstance(mContext).isEnabled();
final boolean oldIncludeForAccessibility = accessibilityEnabled && includeForAccessibility();
int old = mViewFlags;
mViewFlags = (mViewFlags & ~mask) | (flags & mask);
int changed = mViewFlags ^ old;
if (changed == 0)
return;
int privateFlags = mPrivateFlags;
/* Check if the FOCUSABLE bit has changed */
if (((changed & FOCUSABLE_MASK) != 0) &&
((privateFlags & PFLAG_HAS_BOUNDS) !=0))
if (((old & FOCUSABLE_MASK) == FOCUSABLE)
&& ((privateFlags & PFLAG_FOCUSED) != 0))
/* Give up focus if we are no longer focusable */
clearFocus();
else if (((old & FOCUSABLE_MASK) == NOT_FOCUSABLE)
&& ((privateFlags & PFLAG_FOCUSED) == 0))
/*
* Tell the view system that we are now available to take focus
* if no one else already has it.
*/
if (mParent != null) mParent.focusableViewAvailable(this);
final int newVisibility = flags & VISIBILITY_MASK;
if (newVisibility == VISIBLE)
if ((changed & VISIBILITY_MASK) != 0)
/*
* If this view is becoming visible, invalidate it in case it changed while
* it was not visible. Marking it drawn ensures that the invalidation will
* go through.
*/
mPrivateFlags |= PFLAG_DRAWN;
invalidate(true);
needGlobalAttributesUpdate(true);
// a view becoming visible is worth notifying the parent
// about in case nothing has focus. even if this specific view
// isn't focusable, it may contain something that is, so let
// the root view try to give this focus if nothing else does.
if ((mParent != null) && (mBottom > mTop) && (mRight > mLeft))
mParent.focusableViewAvailable(this);
/* Check if the GONE bit has changed */
if ((changed & GONE) != 0)
needGlobalAttributesUpdate(false);
requestLayout();
if (((mViewFlags & VISIBILITY_MASK) == GONE))
if (hasFocus()) clearFocus();
clearAccessibilityFocus();
destroyDrawingCache();
if (mParent instanceof View)
// GONE views noop invalidation, so invalidate the parent
((View) mParent).invalidate(true);
// Mark the view drawn to ensure that it gets invalidated properly the next
// time it is visible and gets invalidated
mPrivateFlags |= PFLAG_DRAWN;
if (mAttachInfo != null)
mAttachInfo.mViewVisibilityChanged = true;
/* Check if the VISIBLE bit has changed */
if ((changed & INVISIBLE) != 0)
needGlobalAttributesUpdate(false);
/*
* If this view is becoming invisible, set the DRAWN flag so that
* the next invalidate() will not be skipped.
*/
mPrivateFlags |= PFLAG_DRAWN;
if (((mViewFlags & VISIBILITY_MASK) == INVISIBLE))
// root view becoming invisible shouldn't clear focus and accessibility focus
if (getRootView() != this)
if (hasFocus()) clearFocus();
clearAccessibilityFocus();
if (mAttachInfo != null)
mAttachInfo.mViewVisibilityChanged = true;
if ((changed & VISIBILITY_MASK) != 0)
// If the view is invisible, cleanup its display list to free up resources
if (newVisibility != VISIBLE && mAttachInfo != null)
cleanupDraw();
if (mParent instanceof ViewGroup)
((ViewGroup) mParent).onChildVisibilityChanged(this,
(changed & VISIBILITY_MASK), newVisibility);
((View) mParent).invalidate(true);
else if (mParent != null)
mParent.invalidateChild(this, null);
if (mAttachInfo != null)
dispatchVisibilityChanged(this, newVisibility);
// Aggregated visibility changes are dispatched to attached views
// in visible windows where the parent is currently shown/drawn
// or the parent is not a ViewGroup (and therefore assumed to be a ViewRoot),
// discounting clipping or overlapping. This makes it a good place
// to change animation states.
if (mParent != null && getWindowVisibility() == VISIBLE &&
((!(mParent instanceof ViewGroup)) || ((ViewGroup) mParent).isShown()))
dispatchVisibilityAggregated(newVisibility == VISIBLE);
notifySubtreeAccessibilityStateChangedIfNeeded();
if ((changed & WILL_NOT_CACHE_DRAWING) != 0)
destroyDrawingCache();
if ((changed & DRAWING_CACHE_ENABLED) != 0)
destroyDrawingCache();
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
invalidateParentCaches();
if ((changed & DRAWING_CACHE_QUALITY_MASK) != 0)
destroyDrawingCache();
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
if ((changed & DRAW_MASK) != 0)
if ((mViewFlags & WILL_NOT_DRAW) != 0)
if (mBackground != null
|| (mForegroundInfo != null && mForegroundInfo.mDrawable != null))
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
else
mPrivateFlags |= PFLAG_SKIP_DRAW;
else
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
requestLayout();
invalidate(true);
if ((changed & KEEP_SCREEN_ON) != 0)
if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes)
mParent.recomputeViewAttributes(this);
if (accessibilityEnabled)
if ((changed & FOCUSABLE_MASK) != 0 || (changed & VISIBILITY_MASK) != 0
|| (changed & CLICKABLE) != 0 || (changed & LONG_CLICKABLE) != 0
|| (changed & CONTEXT_CLICKABLE) != 0)
if (oldIncludeForAccessibility != includeForAccessibility())
notifySubtreeAccessibilityStateChangedIfNeeded();
else
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
else if ((changed & ENABLED_MASK) != 0)
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
以上是关于位运算在Android中的应用的主要内容,如果未能解决你的问题,请参考以下文章