文本出处:http://blog.csdn.net/feelang/article/details/49095235
Android Annotations
Annotations 可以帮助你写出更有意义的契约,它的表现力要大于注释和文档,而且 Android Studio 可以利用这些 Annotations 帮你检测出潜伏的bug。
国际惯例,使用之前,添加依赖包:
- 1
- 1
空值标记
NonNull
和 Nullable
是一对,通过源码的 @Target
可以看出,它们可以出现在方法声明、参数声明以及成员变量的声明。
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
下面这段代码,setItemId(null)
肯定会导致 Crash,IDE 也没有提示 bug 风险。
作为 setItemId
方法本身,为了保证安全,需要对参数 itemId
做一次判空处理:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
调用方并不知道方法内部做了判空处理,很有可能又做了一次判空操作。明明只需要一次判空就OK了,由于信息不对称,导致调用方和提供方各自进行了一次判空操作,造成性能开销。
但是,如果给参数 final String itemId
加上 NonNull
,方法提供方就可以放心大胆地不进行判空操作,因为已经通过 annotation 订立了使用”契约” - 参数不能为空。
IED 也提示了 bug 风险。
Nullable
也就意味着调用方无需再进行判空操作,道理是一样的。
资源标记
当 API 参数是一个表示资源 ID 的 int 时,可以加上资源标记。
看一下 StringRes
的定义,支持的类型除了方法、参数、成员变量之外,甚至还支持 局部变量。
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
资源标记除了针对任何资源都是用的 @AnyRes
,还包括每种资源所对应的特有的标记。
@AnimatorRes
, @AnimRes
, @ArrayRes
, @AttrRes
, @BoolRes
, @ColorRes
, @DimenRes
, @DrawableRes
, @FractionRes
,@IdRes
, @IntegerRes
, @InterpolatorRes
, @LayoutRes
, @MenuRes
, @PluralsRes
, @RawRes
, @StringRes
,@StyleableRes
, @StyleRes
, @XmlRes
.
值标记
与资源标记类似,我们还可以使用 @ColorInt
来标记一个表示颜色值 int 变量。甚至还还可以通过 @FloatRang
和@IntRange
来表示变量的取值范围。
Proguard
Fresco 中使用 @DoNotStrip
来防止混淆,并在 proguard 文件中添加了一条规则:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
现在 Android Annotations 也提供了一个类似的标记 @Keep
,与 @DoNotStrip
相比其优势在于可以不用添加 Proguard 规则,Android 的 Gradle 插件自动会帮我们完成这步操作(还在开发中)。
Thread Annotations
Thread Annotations 有四位成员 - @UiThread
、@MainThread
、@WorkerThread
、@BinderThread
,它们来自不同的Java 文件,却拥有着共同的 target,不信你看:
- 1
- 2
- 1
- 2
如果标记 class,那么这个 class 的所有方法都必须在指定线程上执行,例如
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
这样一来,NavigationBar 的所有方法都要运行在 Ui Thread,否则 Android Studio 会!报!错!
@MainThread
与 @UiThread
的区别比较微妙,首先放放链接 - Support Annotations。总结起来一句话:@MainThread
用于标记与生命周期相关的方法,@UiThread
用于 View Hierarchy,但是 Android Studio 认为两者是可以互换的,所以有这两种标记的方法可以互相调用。
然后我再稍微补充几句:
一个进程有且只有一个主线程 - @MainThread,同时它也是一个 @UiThread。例如,activity 的 main window 就运行在 @MainThread 上,但是系统也允许应用创建其他的线程以运行不同的 window(除了系统进程会这么干,几乎没有其他场景)
@CheckResult
这个 Annotation 对于【只看方法名无法判断是否有返回值】的方法特别管用,例如:
- 1
- 1
只看方法名openUp
,可能不会多想,调用一下就完事了,也不会判断返回值。
但是如果必须要判断 openUp
的返回值,除了文档约束好像也没有其他办法,有了 @CheckResults
,一切都变得简单明了。
@CallSuper
如果你提供了 api 给别人用,但是这个 api 必须要先调用父类方法才能正确执行,怎么办?让 @CallSuper
来拯救你。
Enumerated Annotations
编写高性能代码,从不用 enum 开始!
每一个 enum 都是一个对象,无论从内存还是从性能上来看,都没有一个 primitive type 的变量效率高。
例如,定义一个类 - ActionBar
,用户可以通过 setMode
设置不同的模式,如果使用 enum,可能会写成下面这样:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
为了提升代码效率,可以用两个整形变量来替代 Theme
,但是这样无法保证参数 theme
的合法性,用户可能传入了DARK
和 LIGHT
之外的值。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
如何既能提升效率又可以保证传入参数的合法性呢?
用 @IntDef
来添加一个约束就搞定了。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
如果用户调用 setTheme 时传入了非法参数,Android Studio 会!报!错!
如果我们打算把 DARK
和 LIGHT
作为一个 flag,允许用户通过逻辑运算去自由组合,也就说可以这样调用setTheme
。
- 1
- 2
- 1
- 2
只需要把 @IntDef
的 flag
属性设为 true
即可。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
@StringDef
与 @IntDef
的原理一样,只不过用到的常量是 String 类型。