概述

Java语言提供了Annotation的机制,让描述性的元数据能够和代码共存。通常我们可以利用Annotation,来做一些标志性的说明。然而Annotation必须和相应的解析工具一起才能工作。合理的运用Annotation,会带来一些额外的效果。

本文不讨论Annotation的基础语法以及基础使用方法。

Android用Annotation干了什么?

Java这种强类型的语言,在编写代码的时候,编辑器就会通过语法检查,来阻止一些错误的发生。相比于一些弱类型的语言,更安全。Annotation作为元数据而存在,结合上运行在编辑期的解析工具,可以做到更细致的代码检查,可以说是一种对语法检查的扩充吧。

Android就充分利用了这一点,Android在提供了大量的Annotation的同时,也在ADT中提供了操作这些Annotation的工具,主要体现在代码检查阶段。下面我们会详细看一下Android提供的Annotation。这些Annotation不仅可以让代码在编写的时候,少出一些问题,而且可以对一个类、方法等做详细的说明。这些Annotation也可以指导使用API的开发人员,正确的使用API。

Android是如何运用Annotation来进行代码检查的?

Android提供了几大类的Annotation,用来配合其相应的解析工具,来对代码做更严格的检查。

1、强制方法调用类型。

作用:指示所有Override该方法的方法,都需要调用调用该方法。

Annotations:@CallSuper

使用场景:你设计了一个框架,里面有一些基础方法,各子类需要覆写该方法,但是一定要调用父类的方法。这时候可以使用这个Annotation来强制子类调用父类的方法。

        示例代码:

    //父类
@CallSuper
protected void fun() {
System.out.println("parent fun()");
} //子类
@Override
protected void fun() {
super.fun();
}

如果不调用 super.fun(),则会出现提示:Overriding method should call super.fun。

2、指定固定常量型。

作用:指示一个逻辑类型,并且这个逻辑类型的值只能取固定的常量。

Annotations:@IntDef

@StringDef

使用场景:作为一个特定类型的有限个取值,可以使用Enum或者使用常量,但是Enum可以有类型检查,而常量没有。众所周知,因为内存占用的问题,Android不推荐使用Enum,那就只能使用常量,但是常量不能做类型检查,所以Android就提供了这样一种方法,来解决此问题,让指定的属性值只能取定义好的常量。

        示例代码:

    //定义常量
public static final int INT_1 = 1;
public static final int INT_2 = 2; //指定该Annotation描述的对象,只能使用这两个常量
@IntDef({INT_1, INT_2})
public @interface MyType {
} //如果一个方法只想接收这两个常量(常量名称,而不是常量的值)作为参数,那么可以这样使用
private void f (@MyType int time) {
}

如果调用f()时,不传递这两个常量,比如f(2)。那么就会报错:Must be one of: INT_1, INT_2。

3、资源对象的值类型指定型。

作用:指示所描述的参数、字段、返回值必须是指定的资源类型的引用。

Annotations:@AnimatorRes
                              
@AnimRes
                              
@AnyRes
                              
@ArrayRes
                              
@AttrRes
                              
@BoolRes
                              
@ColorRes
                               @DimenRes
                              
@DrawableRes
                              
@FractionRes
                              
@IdRes
                              
@IntegerRes
                              
@InterpolatorRes
                              
@LayoutRes
                              
@MenuRes
                              
@PluralsRes
                              
@RawRes
                              
@StringRes
                              
@StyleableRes
                               @StyleRes
                              
@TransitionRes
                               @XmlRes

使用场景:如果你有一个方法,需要一个 R.id 的值作为参数,或者一个方法,希望返回值是 R.anim
类型的引用,那么这个时候就可以使用这些Annotation。

        示例代码:

    //希望接收一个id作为参数
private void f (@IdRes int id) {
} private void call() {
//但是实际上不小心传递了一个layout进去。
f(R.layout.day_view);
}

那么这个时候就会报错:Expected resource of type id。

4、取值范围型。

作用:指示一个int、float或者double类型的值,合理的取值范围。

Annotations:@FloatRange

@IntRange

使用场景:比如有一个方法,接收一个int型的透明度参数,那么该方法就会希望传进来的值应该是0~255,这时就可以使用该Annotation。

        示例代码:

    //希望接收一个[0, 255]的值作为透明度
private void setAlpha (@IntRange(from = 0, to = 255) int alpha) {
} private void call() {
//但是实际上不小心传递了一个300进去。
setAlpha(300);
}

那么这个时候就会报错:Value must be ≥ 0 and ≤ 255 (was 300)。

5、线程标识型。

作用:指示某个对象、构造器或者方法,应该在指定的类型的线程运行。

Annotations:@BinderThread

@MainThread

@UiThread

@WorkerThread

使用场景:如果有一个方法,比较耗时,所以它应该放在工作线程执行。这时候可以用@WorkerThread来标注该方法,当在主线程调用该方法的时候,就会报错。

示例代码:

    //该方法比较耗时,所以放在子线程执行
@WorkerThread
private void shouldRunOnWorkerThread() {
} @MainThread
private void runOnMainThread() {
//在主线程调用该方法,会报错。
shouldRunOnWorkerThread();
}

那么这个时候就会报错:Method shouldRunOnWorkerThread must be called from the worker thread, currently inferred thread is main。

像 Activity 的onCreate方法,都是被标注为主线程执行的。

    @MainThread
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
.......................;
}

6、空指针指示型。

作用:指示某个参数、返回值等是否可以为空。

Annotations:@NonNull

@Nullable

使用场景:比如一个方法,可能返回一个null,可以使用@Nullable来标注,那么使用该方法的地方,一般都要做null判断。

7、混淆指示类型。

作用:指示被描述的对象,在编译的时候不要被混淆。

Annotations:@Keep

使用场景:当一个方法、属性、对象等,需要被反射使用时,比如通过反射来实现工厂。这时候可以使用该Annotation来标记,这样在编译的时候,就不会被混淆(如果开了混淆的话)。

        示例代码:

//抽象产品,避免被混淆
@Keep
class Product {} //产品A,避免被混淆
@Keep
class ProductA extends Product{} //产品B,避免被混淆
@Keep
class ProductB extends Product{} private Product getProduct(String productName) {
//使用反射创建具体的Product
return ...;
}

8、参数长度限定类型。

作用:指示被描述的对象应该有明确的大小。

Annotations:@Size

使用场景:当一个参数,需要最小的大小时,使用该Annotation,比如,一个数组最少有2个元素、一个String最低长度为3等。

        示例代码:

    //要求param的长度最小为2
private void minSize(@Size(min = 2) String param) {
} private void callMinSize() {
//传递长度只有1的"s"时,会报错
minSize("s");
}

那么这个时候就会报错:Length must be at least 2 (was 1)。

9、其他类型。

作用:指示一个int值是颜色类型的值(AARRGGBB)。

Annotations:@ColorInt

        示例代码:

    //标明该常量代表颜色值
@ColorInt public static final int BLACK = 0xFF000000;
//标明该方法希望接收一个代表颜色值的参数
public setColor(@ColorInt int color) {
}

合理使用Android提供的Annotation来提高代码的质量的更多相关文章

  1. Android 你可能忽略的提高敲代码效率的方式 (转)

    每日推荐 Eyepetizer-in-Kotlin:一款简约的小视频app,带你走进kotlin 作为学习kotlin的一款app,在撸代码的过程中学习kotlin的语法及特性. Eyepetizer ...

  2. Android 命名规范 (提高代码可以读性)

    android文件众多,根据名称来辨别用途很重要,因此命名要规范 这篇文章可参考:Android 命名规范 (提高代码可以读性) 刚接触android的时候,命名都是按照拼音来,所以有的时候想看懂命名 ...

  3. Android 你可能忽略的提高敲代码效率的方式

    Android 你可能忽略的提高敲代码效率的方式

  4. Android 自定义注解(Annotation)

    现在市面上很多框架都有使用到注解,比如butterknife库.EventBus库.Retrofit库等等.也是一直好奇他们都是怎么做到的,注解的工作原理是啥.咱们能不能自己去实现一个简单的注解呢. ...

  5. 提高代码质量 CheckStyle FindBugs PMD

    提高代码质量-工具篇 注:这是一篇翻译文章,原文:How to improve quality and syntax of your Android code,为了理解连贯,翻译过程中我修改了一些陈述 ...

  6. Android Studio 单刷《第一行代码》系列 01 —— 第一战 HelloWorld

    前言(Prologue) 本系列将使用 Android Studio 将<第一行代码>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Android ...

  7. Android Studio 单刷《第一行代码》系列 02 —— 日志工具 LogCat

    前情提要(Previously) 本系列将使用 Android Studio 将<第一行代码>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Andr ...

  8. Android提供了5种方式存储数据:

    --使用SharedPreferences存储数据: --文件存储数据: --SQLite数据库存储数据: --使用ContentProvider存储数据: --网络存储数据: 一:使用SharedP ...

  9. 如何在Android开发中让你的代码更有效率

    最近看了Google IO 2012年的一个视频,名字叫做Doing More With Less: Being a Good Android Citizen,主要是讲如何用少少的几句代码来改善And ...

随机推荐

  1. Day17-18前端学习之路——常用语句资料库

    一.var 与 let 的区别 var: 可以先初始化再声明该变量; 可以根据需要多次声明相同名称的变量 var myName = 'Chris'; var myName = 'Bob'; let m ...

  2. 如何在GitHub预览html

    1.在GitHub中找到需要预览的html文件,点击settings 2.找到GitHub Pages,将其中的source改为master branch,此时出现了下图中的红色链接,打开一个新的网页 ...

  3. Load_file 常用路径

    load_file 常用路径 WINDOWS下: c:/boot.ini //查看系统版本 c:/windows/php.ini //php配置信息 c:/windows/my.ini //MYSQL ...

  4. 全文检索Lucene框架---查询索引

    一. Lucene索引库查询 对要搜索的信息创建Query查询对象,Lucene会根据Query查询对象生成最终的查询语法,类似关系数据库Sql语法一样Lucene也有自己的查询语法,比如:“name ...

  5. springcloud vue.js 前后分离 微服务 分布式 activiti工作流 集成代码生成器 shiro权限

    1.代码生成器: [正反双向](单表.主表.明细表.树形表,快速开发利器)freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本.处理类.service等完整模块2. ...

  6. java工作流系统jflow表单引擎字段扩展组件介绍

    关键词:工作流快速开发平台  工作流流设计  业务流程管理   asp.net 开源工作流  bpm工作流系统  java工作流主流框架  自定义工作流引擎 表单设计器  流程设计器 装饰类图片 用于 ...

  7. wifite硬核破解WiFi密码

    如题 楼主在这里分享下如何使用工具破解附近的WiFi 今天使用的工具是 wifite 现在都有WiFi万能钥匙了 暴力破解还有市场吗? 首先他俩的破解思路就不一样 wifi万能钥匙是根据云端数据库内容 ...

  8. JS事件流、DOM事件流、IE事件处理、跨浏览器事件处理、事件对象与类型

    事件的移除 removeEventListener() 第二个参数需要指定要移除的事件句柄,不能是匿名函数,因为无法识别 想要移除成功,那么三个参数必须跟addEventListener中的三个完全一 ...

  9. [转]Android Adapter以及getView()方法的理解

    Android Adapter基本理解: 我的理解是: 1.一个有许多getter的类(就是getView(),getCount()....这些方法) 2.有多少个get方法?都是什么? 这些gett ...

  10. 关于非旋转Treap

    刚刚跟着EM-LGH大佬学了非旋转Treap 非常庆幸不用再写万恶的rotate了(来自高级数据结构的恶意) 来记一下 Treap 概念 简单来说,\(Tree_{二叉搜索树} * Heap_堆 = ...