前言 记录一些阅读代码的一些技巧和知识点
这个方法到底是哪里调用的? 平日在阅读代码的时候,经常被跳来跳去的函数(或者是方法)调用栈绕晕,尤其是遇到多态和接口的时候,方法的实际执行的类和实现跟方法定义的位置很难通过 IDE 的跳转关系理清。一不小心就会把自己绕进去,好不容易理清了吧,时间久了再次看的时候又得理一遍,而且有些调用链特别长,那么有没有什么办法可以快速的知道方法调用栈呢?
其实我们可以借助 Exception (准确来说是 Throwable) 的 printStackTrace()
方法打印调用栈。我们知道在发生异常的时候,一般会调用 e.printStackTrace() 打印错误信息,帮助我们定位到发生异常的位置。其实,我们也可以主动创建 Exception 对象去打印方法调用栈。
比如在 Activity
的 onCreate()
方法中,我们想知道 Activity 到底是如何创建的。
方法调用栈工具类 我们首先创建一个工具类,方便复用
1 2 3 4 5 6 7 object SystemTools { fun printMethodTrace (tag: String ) { val trace = Exception(tag) trace.printStackTrace() } }
Activity 生命周期调用链 然后在 Activity 的生命周期中调用工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 class WrapContentActivity : BaseActivity () { override fun onCreate (savedInstanceState: Bundle ?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_wrap_content) SystemTools.printMethodTrace("onCreate" ) } override fun onResume () { super .onResume() SystemTools.printMethodTrace("onResume" ) } }
看一下输出结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 com.engineer.android.mini W: java.lang.Exception: onCreate com.engineer.android.mini W: at com.engineer.android.mini.util.SystemTools.printMethodTrace(SystemTools.kt:17) com.engineer.android.mini W: at com.engineer.android.mini.ui.pure.WrapContentActivity.onCreate(CustomViewPlayGround.kt:357) com.engineer.android.mini W: at android.app.Activity.performCreate(Activity.java:8000) com.engineer.android.mini W: at android.app.Activity.performCreate(Activity.java:7984) com.engineer.android.mini W: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309) com.engineer.android.mini W: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422) com.engineer.android.mini W: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601) com.engineer.android.mini W: at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85) com.engineer.android.mini W: at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) com.engineer.android.mini W: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) com.engineer.android.mini W: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) com.engineer.android.mini W: at android.os.Handler.dispatchMessage(Handler.java:106) com.engineer.android.mini W: at android.os.Looper.loop(Looper.java:223) com.engineer.android.mini W: at android.app.ActivityThread.main(ActivityThread.java:7656) com.engineer.android.mini W: at java.lang.reflect.Method.invoke(Native Method) com.engineer.android.mini W: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) com.engineer.android.mini W: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
可以看到 ActivityThread.java(2066)行,内部类 H 接收到 Message 消息后,便开始了方法调用链,包括 LaunchActivityItem.execute
,handleLaunchActivity
,performLaunchActivity
,Instrumentation.callActivityOnCreate
,performCreate
等我们在 AMS 流程中经常看到这些方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 com.engineer.android.mini W: java.lang.Exception: onResume com.engineer.android.mini W: at com.engineer.android.mini.util.SystemTools.printMethodTrace(SystemTools.kt:17) com.engineer.android.mini W: at com.engineer.android.mini.ui.pure.WrapContentActivity.onResume(CustomViewPlayGround.kt:351) com.engineer.android.mini W: at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1456) com.engineer.android.mini W: at android.app.Activity.performResume(Activity.java:8135) com.engineer.android.mini W: at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4434) com.engineer.android.mini W: at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4476) com.engineer.android.mini W: at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52) com.engineer.android.mini W: at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176) com.engineer.android.mini W: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) com.engineer.android.mini W: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066) com.engineer.android.mini W: at android.os.Handler.dispatchMessage(Handler.java:106) com.engineer.android.mini W: at android.os.Looper.loop(Looper.java:223) com.engineer.android.mini W: at android.app.ActivityThread.main(ActivityThread.java:7656) com.engineer.android.mini W: at java.lang.reflect.Method.invoke(Native Method) com.engineer.android.mini W: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) com.engineer.android.mini W: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
onResume 的调用也是类似 onCreate 都是由 ActivityThread
处理 Message 消息开始。
View measure 可以再来看一个大家比较熟悉的 View measure 流程的代码。可以先思考一下,一个继承自View.java 的自定义 View 。其 OnMeasure 至少会执行多少次?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class SimpleViewOne @JvmOverloads constructor ( context: Context, attributeSet: AttributeSet? = null , style: Int = 0 ) : View(context, attributeSet, style) { override fun onMeasure (widthMeasureSpec: Int , heightMeasureSpec: Int ) { super .onMeasure(widthMeasureSpec, heightMeasureSpec) SystemTools.printMethodTrace("SimpleViewOne" ) } }
完整代码 输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 2021-08-09 21:46:33.516 com.engineer.android.mini W: java.lang.Exception: SimpleViewOne 2021-08-09 21:46:33.516 com.engineer.android.mini W: at com.engineer.android.mini.util.SystemTools.printMethodTrace(SystemTools.kt:17) 2021-08-09 21:46:33.516 com.engineer.android.mini W: at com.engineer.android.mini.ui.pure.SimpleViewOne.onMeasure(CustomViewPlayGround.kt:86) 2021-08-09 21:46:33.516 com.engineer.android.mini W: at android.view.View.measure(View.java:25466) 2021-08-09 21:46:33.516 com.engineer.android.mini W: at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957) 2021-08-09 21:46:33.516 com.engineer.android.mini W: at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552) 2021-08-09 21:46:33.516 com.engineer.android.mini W: at android.widget.LinearLayout.measureVertical(LinearLayout.java:842) 2021-08-09 21:46:33.516 com.engineer.android.mini W: at android.widget.LinearLayout.onMeasure(LinearLayout.java:721) 2021-08-09 21:46:33.516 com.engineer.android.mini W: at android.view.View.measure(View.java:25466) 2021-08-09 21:46:33.516 com.engineer.android.mini W: at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957) 2021-08-09 21:46:33.517 com.engineer.android.mini W: at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) 2021-08-09 21:46:33.517 com.engineer.android.mini W: at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:145) 2021-08-09 21:46:33.517 com.engineer.android.mini W: at android.view.View.measure(View.java:25466) 2021-08-09 21:46:33.517 com.engineer.android.mini W: at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957) 2021-08-09 21:46:33.517 com.engineer.android.mini W: at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552) 2021-08-09 21:46:33.518 com.engineer.android.mini W: at android.widget.LinearLayout.measureVertical(LinearLayout.java:842) 2021-08-09 21:46:33.518 com.engineer.android.mini W: at android.widget.LinearLayout.onMeasure(LinearLayout.java:721) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at android.view.View.measure(View.java:25466) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at android.view.View.measure(View.java:25466) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at android.widget.LinearLayout.measureVertical(LinearLayout.java:842) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at android.widget.LinearLayout.onMeasure(LinearLayout.java:721) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at android.view.View.measure(View.java:25466) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at com.android.internal.policy.DecorView.onMeasure(DecorView.java:747) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at android.view.View.measure(View.java:25466) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:3397) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:2228) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2486) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1952) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8171) 2021-08-09 21:46:33.519 com.engineer.android.mini W: at android.view.Choreographer$CallbackRecord.run(Choreographer.java:972) 2021-08-09 21:46:33.520 com.engineer.android.mini W: at android.view.Choreographer.doCallbacks(Choreographer.java:796) 2021-08-09 21:46:33.520 com.engineer.android.mini W: at android.view.Choreographer.doFrame(Choreographer.java:731) 2021-08-09 21:46:33.520 com.engineer.android.mini W: at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957) 2021-08-09 21:46:33.520 com.engineer.android.mini W: at android.os.Handler.handleCallback(Handler.java:938) 2021-08-09 21:46:33.520 com.engineer.android.mini W: at android.os.Handler.dispatchMessage(Handler.java:99) 2021-08-09 21:46:33.520 com.engineer.android.mini W: at android.os.Looper.loop(Looper.java:223) 2021-08-09 21:46:33.520 com.engineer.android.mini W: at android.app.ActivityThread.main(ActivityThread.java:7656) 2021-08-09 21:46:33.520 com.engineer.android.mini W: at java.lang.reflect.Method.invoke(Native Method) 2021-08-09 21:46:33.520 com.engineer.android.mini W: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 2021-08-09 21:46:33.520 com.engineer.android.mini W: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 2021-08-09 21:46:33.564 com.engineer.android.mini W: java.lang.Exception: SimpleViewOne 2021-08-09 21:46:33.565 com.engineer.android.mini W: at com.engineer.android.mini.util.SystemTools.printMethodTrace(SystemTools.kt:17) 2021-08-09 21:46:33.565 com.engineer.android.mini W: at com.engineer.android.mini.ui.pure.SimpleViewOne.onMeasure(CustomViewPlayGround.kt:86) 2021-08-09 21:46:33.565 com.engineer.android.mini W: at android.view.View.measure(View.java:25466) 2021-08-09 21:46:33.565 com.engineer.android.mini W: at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957) 2021-08-09 21:46:33.565 com.engineer.android.mini W: at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552) 2021-08-09 21:46:33.565 com.engineer.android.mini W: at android.widget.LinearLayout.measureVertical(LinearLayout.java:842) 2021-08-09 21:46:33.565 com.engineer.android.mini W: at android.widget.LinearLayout.onMeasure(LinearLayout.java:721) 2021-08-09 21:46:33.565 com.engineer.android.mini W: at android.view.View.measure(View.java:25466) 2021-08-09 21:46:33.565 com.engineer.android.mini W: at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957) 2021-08-09 21:46:33.565 com.engineer.android.mini W: at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) 2021-08-09 21:46:33.565 com.engineer.android.mini W: at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:145) 2021-08-09 21:46:33.565 com.engineer.android.mini W: at android.view.View.measure(View.java:25466) 2021-08-09 21:46:33.565 com.engineer.android.mini W: at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957) 2021-08-09 21:46:33.565 com.engineer.android.mini W: at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552) 2021-08-09 21:46:33.566 com.engineer.android.mini W: at android.widget.LinearLayout.measureVertical(LinearLayout.java:842) 2021-08-09 21:46:33.566 com.engineer.android.mini W: at android.widget.LinearLayout.onMeasure(LinearLayout.java:721) 2021-08-09 21:46:33.566 com.engineer.android.mini W: at android.view.View.measure(View.java:25466) 2021-08-09 21:46:33.566 com.engineer.android.mini W: at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957) 2021-08-09 21:46:33.566 com.engineer.android.mini W: at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) 2021-08-09 21:46:33.566 com.engineer.android.mini W: at android.view.View.measure(View.java:25466) 2021-08-09 21:46:33.566 com.engineer.android.mini W: at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957) 2021-08-09 21:46:33.566 com.engineer.android.mini W: at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552) 2021-08-09 21:46:33.566 com.engineer.android.mini W: at android.widget.LinearLayout.measureVertical(LinearLayout.java:842) 2021-08-09 21:46:33.566 com.engineer.android.mini W: at android.widget.LinearLayout.onMeasure(LinearLayout.java:721) 2021-08-09 21:46:33.566 com.engineer.android.mini W: at android.view.View.measure(View.java:25466) 2021-08-09 21:46:33.566 com.engineer.android.mini W: at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6957) 2021-08-09 21:46:33.566 com.engineer.android.mini W: at android.widget.FrameLayout.onMeasure(FrameLayout.java:194) 2021-08-09 21:46:33.566 com.engineer.android.mini W: at com.android.internal.policy.DecorView.onMeasure(DecorView.java:747) 2021-08-09 21:46:33.567 com.engineer.android.mini W: at android.view.View.measure(View.java:25466) 2021-08-09 21:46:33.567 com.engineer.android.mini W: at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:3397) 2021-08-09 21:46:33.567 com.engineer.android.mini W: at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2880) 2021-08-09 21:46:33.567 com.engineer.android.mini W: at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1952) 2021-08-09 21:46:33.567 com.engineer.android.mini W: at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8171) 2021-08-09 21:46:33.567 com.engineer.android.mini W: at android.view.Choreographer$CallbackRecord.run(Choreographer.java:972) 2021-08-09 21:46:33.567 com.engineer.android.mini W: at android.view.Choreographer.doCallbacks(Choreographer.java:796) 2021-08-09 21:46:33.567 com.engineer.android.mini W: at android.view.Choreographer.doFrame(Choreographer.java:731) 2021-08-09 21:46:33.567 com.engineer.android.mini W: at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957) 2021-08-09 21:46:33.567 com.engineer.android.mini W: at android.os.Handler.handleCallback(Handler.java:938) 2021-08-09 21:46:33.567 com.engineer.android.mini W: at android.os.Handler.dispatchMessage(Handler.java:99) 2021-08-09 21:46:33.567 com.engineer.android.mini W: at android.os.Looper.loop(Looper.java:223) 2021-08-09 21:46:33.567 com.engineer.android.mini W: at android.app.ActivityThread.main(ActivityThread.java:7656) 2021-08-09 21:46:33.567 com.engineer.android.mini W: at java.lang.reflect.Method.invoke(Native Method) 2021-08-09 21:46:33.567 com.engineer.android.mini W: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 2021-08-09 21:46:33.567 com.engineer.android.mini W: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
可以看到 onMeasure 是会执行两次的。并不是日志重复了,仔细看的话可以发现两次的调用链是有差异的
调用栈是按方法压栈的顺序打印的,所以需要倒着看。
我们只关注到 DecorView 的 onMeasure 之前的调用链,因为后面一定是相同的
第一次是
1 2 3 4 5 6 com.android.internal.policy.DecorView.onMeasure(DecorView.java:747 ) android.view.View.measure(View.java:25466 ) android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:3397 ) android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:2228 ) android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2486 ) android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1952 )
第二次是
1 2 3 4 5 com.android.internal.policy.DecorView.onMeasure(DecorView.java:747 ) android.view.View.measure(View.java:25466 ) android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:3397 ) android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2880 ) android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1952 )
可以看到两次 performMeaure 是通过不同的方式发起的。
可以看到,利用 Throwable 的 printMethodTrace()
方法。我们可以非常方便的获得复杂的方法调用链。这样的技巧不仅可以用在定位错误,阅读复杂源码的场景。也可以用在我们日常开发中,尤其是在方法调用链比较长,且包含接口、抽象类的时候,使用这个方法可以非常方便的让我们确定方法调用关键结点,甚至是行号。使用这个在日常开发中做很多事情,这里就不一一举例了,有兴趣的话可以自己尝试一下。
位运算的逻辑其实很简 我们经常在源码中看到使用位运算表达的逻辑。比如在 View 的 measure
方法中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public final void measure (int widthMeasureSpec, int heightMeasureSpec) { long key = (long ) widthMeasureSpec << 32 | (long ) heightMeasureSpec & 0xffffffffL ; if (mMeasureCache == null ) mMeasureCache = new LongSparseLongArray(2 ); final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT; if (forceLayout || needsLayout) { mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET; resolveRtlPropertiesIfNeeded(); int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key); if (cacheIndex < 0 || sIgnoreMeasureCache) { onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } else { long value = mMeasureCache.valueAt(cacheIndex); setMeasuredDimensionRaw((int ) (value >> 32 ), (int ) value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) { throw new IllegalStateException("View with id " + getId() + ": " + getClass().getName() + "#onMeasure() did not set the" + " measured dimension by calling" + " setMeasuredDimension()" ); } mPrivateFlags |= PFLAG_LAYOUT_REQUIRED; } mOldWidthMeasureSpec = widthMeasureSpec; mOldHeightMeasureSpec = heightMeasureSpec; mMeasureCache.put(key, ((long ) mMeasuredWidth) << 32 | (long ) mMeasuredHeight & 0xffffffffL ); }
可以看到这么几行代码,大量使用 逻辑运算符进行了各种逻辑判断和处理,看的时候总是有种似懂非懂的感觉,这里就来简单总结一下。
在之前 二进制 一文中,其实就二进制相关的运算的一些基础做过介绍,这里就结合一个具体的例子加深一下印象。
鸡蛋灌饼你要加点啥? 在 FantasyCake 中可以添加土豆丝、海带丝、生菜形式不同 style 的 cake。 同时在结算的时候会根据输入金额的限制去除掉某一项。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public class FantasyCake { public static final int FLAG_ADD_POTATO_SHREDS = 0x00000001 ; public static final int FLAG_ADD_SEAWEED_STRIPS = 0x00000002 ; public static final int FLAG_ADD_LETTUCE = 0x00000004 ; private int style = 0 ; @Override public String toString () { if ((this .style & FLAG_ADD_SEAWEED_STRIPS) != FLAG_ADD_SEAWEED_STRIPS) { System.err.println("without add seaweed_strips" ); } return "FantasyCake{" + "style=" + Integer.toBinaryString(this .style) + '}' ; } public void addLettuce () { this .style |= FLAG_ADD_LETTUCE; } public void addPotatoShreds () { this .style |= FLAG_ADD_POTATO_SHREDS; } public void addSeaweedStrips () { this .style |= FLAG_ADD_SEAWEED_STRIPS; } public int checkout (int money) { int base = 5 ; if ((style & FLAG_ADD_LETTUCE) == FLAG_ADD_LETTUCE) { base = base + 1 ; } if ((style & FLAG_ADD_SEAWEED_STRIPS) == FLAG_ADD_SEAWEED_STRIPS) { base = base + 2 ; } if ((style & FLAG_ADD_POTATO_SHREDS) == FLAG_ADD_POTATO_SHREDS) { base = base + 3 ; } if (base > money) { style &= ~FLAG_ADD_POTATO_SHREDS; return checkout(money); } return base; } }
我们可以测试一下 ,完整代码参见 FantasyCake.java
1 2 3 4 5 6 7 8 9 10 11 fun main () { val cake = FantasyCake() println(cake) cake.addLettuce() cake.addPotatoShreds() cake.addSeaweedStrips() println(cake) println("cost = ${cake.checkout(9 )} " ) println(cake) }
输出
1 2 3 4 5 without add seaweed_strips FantasyCake{style=0} FantasyCake{style=111} cost = 8 FantasyCake{style=110}
可以看到当 style 缺失 FLAG_ADD_SEAWEED_STRIPS 会输出警告。
默认的 style 为 0
添加 3 种 FLAG 后,style = 111 (符合预期)
结算时,由于总额大于 9 ,去除了 FLAG_ADD_POTATO_SHREDS, 并重新结算结果为 8 。
至此可以简单总结规律:
A |= FLAG_ANY
是对 A 进行写 FLAG_ANY 对应位的 1
A &= FLAG_ANY
是对 A 进行写 FLAG_ANY 对应位的 0
(A & FLAG_ANY) == FLAG_ANY)
确保 A 有 FLAG_ANY 对应位的 1
(A & FLAG_ANY) != FLAG_ANY)
确保 A 没有 FLAG_ANY 对应位的 1,即为 0。
还有常见的一种
(A & FLAG_ANY) != 0
则表明在 A 当中,至少 FLAG_ANY 对应位的值是 1。这一标志位是开启的。
在 Android 源码中,FLAG_XXX 常常对应的就是某个功能是否开启了,下次阅读源码的时候,按照上面的方式理解就好了。
可以看到使用位运算有一个 64 字节的 Int 类型就相当于可以包含 64 个标志位,相比直接使用 boolean 值节省了不少内存。同时一旦习惯了这种用法会觉得写起来更方便。
maybe continued