利用 Android Annotations 来玩玩契约编程

关于契约编程

契约编程Contract Programming),我个人理解就好像商业行为一样,买卖双方彼此订立好契约,相互遵守,互利共赢,如果有一方没有遵守契约,那就意味着买卖可能不成立。

这套逻辑同样也适用于开发,提供 API 的一方不但要完成功能,还要尽可能地写清楚使用条件(相当于契约),文档或注释是一种形式,但是我们无法判断使用者到底有没有遵守约定。

举个栗子,定义一个方法 setItemId,参数为 String itemId

void setItemId(final String itemId)
  • 1

那么,如何保证 Fail-safe 呢?当然最简单的方法就是在方法体内判断一下 itemId 是否为空。

void setItemId(final String itemId) {
    if (TextUtils.isEmpty(itemId)) return;
    ....
}
  • 1
  • 2
  • 3
  • 4

这样一来,虽然保证了 setItemId 的安全性,但同时也带来了新问题,当参数不合法时,无法及时暴露问题,这样可能会给 Debug 带来一定困难。fresco 的做法比较直接,如果参数不合法,直接抛异常。

public void addControllerListener(ControllerListener<? super INFO> controllerListener) {
    Preconditions.checkNotNull(controllerListener);
    ...
}
  • 1
  • 2
  • 3
  • 4

其中 Preconditions 是 guava 提供的一个类,用于判断变量是否符合前置条件,如果不符合则直接抛异常,及时地暴露问题。这也符合 infer 提倡的编码习惯:

Program safely, annotate nothing!

翻译成土话就是『安全编码,啥也不标』,如果没有任何标记,则意味着参数不能空,如果为空,不好意思,直接抛异常。但是如果可以接受参数为 null 时,怎样通知调用方呢?这时候就要用到 Annotations 了,这个我们下面再讲。

『安全编码,啥也不标』这种习惯比较适用于会写单元测试的开发者,但是像我这种喜欢偷懒,不写单测,还追求极致性能的码农来说,面对setItemId(final String itemId) 这样的API,很容易变得纠结,因为很想知道它到底资瓷不资瓷空值,如果支持,那我就不需要再判空了。幸好有Android Annotations, 她让这件事变得比较简单。

Android Annotations

Annotations 可以帮助你写出更有意义的契约,它的表现力要大于注释和文档,而且 Android Studio 可以利用这些 Annotations 帮你检测出潜伏的bug。

国际惯例,使用之前,添加依赖包:

compile 'com.android.support:support-annotations:23.0.1
  • 1

空值标记

NonNull 和 Nullable 是一对,通过源码的 @Target 可以看出,它们可以出现在方法声明、参数声明以及成员变量的声明。

@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD})
public @interface Nullable {
}
  • 1
  • 2
  • 3
  • 4

下面这段代码,setItemId(null) 肯定会导致 Crash,IDE 也没有提示 bug 风险。

作为 setItemId 方法本身,为了保证安全,需要对参数 itemId 做一次判空处理:

public void setItemId(final String itemId) {
    if (!TextUtils.isEmpty(itemId)) {
        Toast.makeText(this, itemId, Toast.LENGTH_SHORT).show();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5

调用方并不知道方法内部做了判空处理,很有可能又做了一次判空操作。明明只需要一次判空就OK了,由于信息不对称,导致调用方和提供方各自进行了一次判空操作,造成性能开销。

但是,如果给参数 final String itemId 加上 NonNull,方法提供方就可以放心大胆地不进行判空操作,因为已经通过 annotation 订立了使用”契约” -参数不能为空

IED 也提示了 bug 风险。

Nullable 也就意味着调用方无需再进行判空操作,道理是一样的。

资源标记

当 API 参数是一个表示资源 ID 的 int 时,可以加上资源标记。

看一下 StringRes 的定义,支持的类型除了方法、参数、成员变量之外,甚至还支持 局部变量。

@Documented
@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
public @interface StringRes {
}
  • 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 文件中添加了一条规则:

 # Do not strip any method/class that is annotated with @DoNotStrip
 -keep @com.facebook.common.internal.DoNotStrip class *
 -keepclassmembers class * {
     @com.facebook.common.internal.DoNotStrip *;
 }
  • 1
  • 2
  • 3
  • 4
  • 5

现在 Android Annotations 也提供了一个类似的标记 @Keep,与 @DoNotStrip 相比其优势在于可以不用添加 Proguard 规则,Android 的 Gradle 插件自动会帮我们完成这步操作(还在开发中)。

其他

出了以上列举的一些常用的之外,还有很多其他的很有用的 Annotation,例如 @CheckResults@CallSuper 等等,具体可以参考 Android 官方文档 -Improving Code Inspection with Annotations

总结

Android Annotations 绝对是个好东西,不但能够提高代码可读性、而且会大大提高健壮性,希望大家编码时能充分利用起来,也欢迎分享心得。

参考文章



关于我

转自:一介码农

时间: 2024-08-01 02:32:56

利用 Android Annotations 来玩玩契约编程的相关文章

怎样用 Android Annotations 写出高性能代码

上一篇博文中简单介绍了 Android Annotations 的基本用法,顺便扯了一下概念 - 契约编程,阅读量少的可怜,看来并没有多少人对此感兴趣,今天再来一篇,介绍几个稍微高级点的用法,我就不信弄不出一个大新闻. 本篇将要介绍的几个也许并不常用,但是逼格是有保证的,它们是 Thread Annotations CheckResult Annotations CallSuper Annotations Enumerated Annotations Thread Annotations Thr

Android 安全加密:Https编程详解_Android

Android安全加密专题文章索引 Android安全加密:对称加密 Android安全加密:非对称加密 Android安全加密:消息摘要Message Digest Android安全加密:数字签名和数字证书 Android安全加密:Https编程 以上学习所有内容,对称加密.非对称加密.消息摘要.数字签名等知识都是为了理解数字证书工作原理而作为一个预备知识.数字证书是密码学里的终极武器,是人类几千年历史总结的智慧的结晶,只有在明白了数字证书工作原理后,才能理解Https 协议的安全通讯机制.

Android 安全加密:Https编程详解

Android安全加密专题文章索引 Android安全加密:对称加密 Android安全加密:非对称加密 Android安全加密:消息摘要Message Digest Android安全加密:数字签名和数字证书 Android安全加密:Https编程 以上学习所有内容,对称加密.非对称加密.消息摘要.数字签名等知识都是为了理解数字证书工作原理而作为一个预备知识.数字证书是密码学里的终极武器,是人类几千年历史总结的智慧的结晶,只有在明白了数字证书工作原理后,才能理解Https 协议的安全通讯机制.

利用Android平台录音,得到的音频严重失真

问题描述 利用Android平台录音,得到的音频严重失真 最近在做Android开发录音方面的开发,利用mediarecord录取声音,得到的音频严重失真,几乎听不出声音的内容,但从声音可以确定的是,这个声音确实是刚才录进去的.开始以为是采样率方面出错,但从打印信息看到,audio系统获取的采样率是8000,通道数是1,调用编码器时识别的采样率也是8000,通道数也是1.现在很迷糊,不知道什么原因导致系统录音失真,请大神帮我定位一下问题. 解决方案 你试试我这段代码看能不能录,public st

利用Android做一个数独游戏

问题描述 利用Android做一个数独游戏 如何在Android中绘制出数独游戏九宫格的画面?是在Java代码中编写?数独游戏的算法如何实现 解决方案 开源项目opensudokuhttp://code.google.com/p/opensudoku-android/

利用android客户端支付宝sdk的jar包和demo的几个处理支付的类再做一个jar包

问题描述 利用android客户端支付宝sdk的jar包和demo的几个处理支付的类再做一个jar包 利用android客户端支付宝sdk的jar包和demo的几个处理支付的类(不包含activity)再做一个jar包,其它项目中只要导入该包和传入几个参数就可以使用支付宝支付功能 解决方案 将项目导出作为一个jar供他人引用不就好了,或者使用webservice

Android 开发者如何使用函数式编程 (二)

本文讲的是Android 开发者如何使用函数式编程 (二), 如果你没有读过第一部分,请到这里读: Android 开发者如何使用函数式编程 (一) 在上一篇帖子中,我们学习了纯粹性*.副作用和排序**.在本部分中,我们将讨论不变性和并发. 不变性 不变性是指一旦一个值被创建,它就不可以被修改. 假设我有一个像这样的 Car 类: public final class Car { private String name; public Car(final String name) { this.

Android开发中的多线程编程技术

Android开发中的多线程编程技术 [IT168技术]多线程这个令人生畏的"洪水猛兽",很多人谈起多线程都心存畏惧.在Android开发过程中,多线程真的很难吗?多线程程序的"麻烦"源于它很抽象.与单线程程序运行模式不同,但只要掌握了它们的区别,编写多线程程序就会很容易了.下面让我们集中精力开始学习吧! 多线程案例--计时器 我在给我的学生讲多线程的时候都会举一个计时器的案例,因为计时器案例是多线程的经典应用. 这个案例中,屏幕启动之后,进入如图8-1所示的界面.

android-关于如何利用Android实现悬浮窗口半隐藏的问题

问题描述 关于如何利用Android实现悬浮窗口半隐藏的问题 如何利用Android实现悬浮窗口半隐藏在屏幕外,半显示在屏幕内部, 当用户点击悬浮窗口时,悬浮窗口就会完整的显示出来,当没有用户点击时, 就会处于半隐藏状态. 解决方案 android悬浮窗口的实现android悬浮窗口的实现android悬浮窗口的实现 解决方案二: 不明白你的需求. 如果只是要悬浮窗大小可变,你可以先展示一个小的悬浮窗,点击它之后就移除小的那个,创建一个大的悬浮窗显示出来