Support Annotation Library
简介
当我们在使用Android Support Library系列库时,经常会有一个叫support-annotation的包也会出现。其实发展至今Android Support Library不再是单独的一个庞大的jar包了,而是由很多独立的子jar包组成,如support-v4、support-v7、design、cardview、compat等,而annotation也是其中之一,它包含一系列很实用的注解,来帮助开发者及时发现问题,利用lint提示,在开发者编码的时候就进行错误提示。一般这个库是不需要我们手动去添加的,因为项目中必不可免的会使用到别的support库,在别的support库中就已经依赖了这个annotation库。
compile 'com.android.support:support-annotations:25.2.0'
注解使用
Nullness
- @Nullable作用于函数参数或返回值,标记参数或返回值为可以空。
- @NonNull作用于函数参数或返回值,标记参数或返回值为不能为空。
用在方法上的注解,提示当前方法的参数列表以及返回值是否可为null,做了注解之后,AS lint在开发者违反了注解标识的使用方式后,就做出相应警告提示。
@NonNull SpannableString getSS(@Nullable String string) {
if (string == null) string = "";
return new SpannableString(string);
}
当违反了注解标识时,就会出现如下的lint提示:
NonNull_lint.png资源类型注解
在封装方法时,经常会将资源的id即int整型作为形参,而我们的资源各种类型的都有,但是在R.java中都是同样的以一个int型数据来标识。这就会出现本来我需要的是一个layout的资源id,传入却是一个string的资源id的错误可能,而这些错误在编译时其实是不报错的,只有在运行时才会报错。或者在团队合作中别的开发者使用你封装的方法时,无法直接确切的知道你这个方法接收的int类型有没有限制,导致需要翻看你的源码去理解。为了避免出现这些错误以及方便使用者明确知道方法的接收类型,就可以使用资源类型注解
/**
* Denotes that an integer parameter, field or method return value is expected
* to be an animator resource reference (e.g. {@code android.R.animator.fade_in}).
*/
@Documented
@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
public @interface AnimatorRes {
}
关于资源类型注解,源码中都是形如此的,可应用于方法、方法参数、成员属性、局部变量的注解。
- @AnimatorRes:R.animation
- @AnimRes:R.anim
- @AnyRes:任何一种资源类型,不建议使用,最好都是指定具体类型
- @ArrayRes:R.array
- @AttrRes:R.attr
- @BoolRes: R.bool
- @ColorRes: R.color
- @DimenRes: R.dimen
- @DrawableRes: R.drawable
- @FractionRes: R.fraction,这个比较少见,这种类型资源常见于Animation Xml中,比如50%p,表示占parent的50%,具体使用及含义点此。
getResources().getFraction(); /** *@param id fraction资源id,值可形如为 5%、5%p 样式 *@param base 当fraction为5%类型时,此参数起作用,最终结果为 fraction*base (即:忽略pbase) *@param pbase 当fraction为5%p类型时,此参数起作用,最终结果为 fraction*pbase (即:忽略base) */ public float getFraction(@FractionRes int id, int base, int pbase) { final TypedValue value = obtainTempTypedValue(); try { mResourcesImpl.getValue(id, value, true); if (value.type == TypedValue.TYPE_FRACTION) { return TypedValue.complexToFraction(value.data, base, pbase); } throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id) + " type #0x" + Integer.toHexString(value.type) + " is not valid"); } finally { releaseTempTypedValue(value); } } public static float complexToFraction(int data, float base, float pbase) { switch ((data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK) { case COMPLEX_UNIT_FRACTION: return complexToFloat(data) * base; case COMPLEX_UNIT_FRACTION_PARENT: return complexToFloat(data) * pbase; } return 0; }
- @IdRes:R. id,id Resource
- @IntegerRes: R.integer
- @InterpolatorRes: R.interpolator,动画中的插值器,一般用于Animation XMl中
- @layoutRes: R.layout
- @MenueRes: R.menu
- @PluralsRes: R.plurals,复数资源。在做APP语言国际化时,可能会用到,具体请参考此文或官方文档
- @RawRes: R.raw
- @StringRes: R.string 官方文档
- @StyleableRes: R.styleable
- @StyleRes: R.style
- ==@TransitionRes: R.transition,具体作用 ???==
- @XmlRes: R.xml
类型定义注解
由于Android中不推荐使用enum,一般都是由int or String 类型的常量来替代。但是这样就缺少了参数传值的限制,使得封装调用容易出错。所以Annotation中添加了自定义类型的注解支持,来解决enum的问题
- @InDef
- @StringDef
/**
* 关于输入控件的输入状态的分类
*/
@IntDef({WatcherType.EMPTY, WatcherType.CHANGING, WatcherType.OUT_LIMIT})
@Retention(RetentionPolicy.SOURCE)//仅在源文件中有效(即.java文件中)
public @interface WatcherType {
int EMPTY = 0;//未输入or默认状态
int CHANGING = 1;//正常输入值的状态
int OUT_LIMIT = 2;//超出输入限制的状态
}
/**
* @return 对返回值的类型做了限制
*/
@WatcherType
int getWacherType() {
return WatcherType.CHANGING;
}
关于上述代码中的@Retention(RetentionPolicy.SOURCE)注解,定义了@WacherType的生命周期,表示次注解仅在源文件代码中生效(非运行时),即自定义此注解的作用仅是在编码时做一个类型限制,减少参数误传等bug的发生。
使用示例如上述代码,当返回值不是按照注解的类型返回时,lint就会标红报错提示:
IntDef_lint.png关于@StringDef的使用,与@IntDef的使用非常类似
线程注解
- @UiThread 用于View相关的注解
- @MainThread 主线程的注解,通常也是UiThread,一般可混用。
- @BinderThread 运行在Binder线程中
- @WorkerThread 后台线程中,也即是自定义创建的子线程、异步请求中。
主线程中针对执行任务的操作,有个5s的限制否则就会ANR。主线程是在APP启动的时候就已经创建了,而Ui线程是针对VeiwRootImpl创建后也即是开始处理View的任务后而对主线程的另一种称呼,在VeiwRootImpl中有一个专门用于检测当前的线程的方法,而UiThread线程的出现主要是为了限制开发者只能在UiThread中更新Ui.
RGB色值注解
-
@ColorInt
- 主要用于标记表示Color色值的int型数据,即通过Color.parseColor()解析出来的int 或者 getResource.getColor()得到的int。
void setSSColor(@ColorInt int color){}
setSSColor(Color.parseColor("#ff00ff"));
值范围注解
对参数传值的范围做限制,通过下面的注解可以设置允许传值的范围or长度
-
@IntRange
void setAmount(@IntRange(from = 10, to = 100) int amount) { }
针对int类型传值做限制
当传入不在允许范围内的参数时,有如下提示:
IntRange_lint.png
-
@FloatRange
void setAmount(@FloatRange(from = 10.0d, to = 100.0d) double amount) { }
使用方式与@IntRange类似,@FloatRange是针对float or double类型的传值(即浮点型数据)进行限制
-
@Size
void setName(@Size(multiple = 2) String name) {} void setStrArr(@Size(2) String [] strArr){} void setStrArr(@Size(min = 5,max = 10) String [] strArr){} void setStrArr(@Size(multiple = 2) String [] strArr){} void setStrList(@Size(min = 1) List<String> list) {}
@Size是针对容器类做出长度的限制,字符串、数组、集合等类型数据都可以使用@Size注解来做限定
- @Size(value) 精确指定长度必须为指定值value
- min 指定最小值
- max 指定最大值
- multiple 指定长度必须为指定值的倍数关系
==针对集合做@Size注解,未达到同样的效果,仅仅在方法参数处做@Size处理,无法让lint提示,必须也同时针对传入的参数也指定@Size,这样才会有提示,但是这样就失去了指定@Size注解的意义了。姿势不对么???==
权限注解
-
@RequiresPermission
指定当前方法需要的权限
-
@RequiresPermission(value) 指定需要某一个权限
@RequiresPermission(android.Manifest.permission.CALL_PHONE) void callPhone(){}
-
anyOf 指定至少需要其中一个权限
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) public abstract Location getLastKnownLocation(String provider);
-
allOf 指定需要的所有权限
@RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) public abstract Location getLastKnownLocation(String provider);
-
针对Content Provider,读和写两个操作,可能需要不同的权限
@RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS)) @RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS)) public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
此注解不可使用在方法上,如下图所示,方法上需要使用前面三种方式做权限注解
RequiresPermission_Write_Read_lint.png
-
-
针对方法参数做权限注解,一般Intent做为形参,主要对Intent的注解。并Intent的具体权限需要是体现在Intent的创建时所用的属性(如action、URI等)上,即创建Intent的属性上有具体权限注解,如下代码所示:
RequiresPermission_Intent_lint.png@RequiresPermission(android.Manifest.permission.CALL_PHONE) public static final String ACTION_CALL = "android.intent.action.CALL"; void callPhone(@RequiresPermission Intent intent) { startActivity(intent); } Intent intent = new Intent(ACTION_CALL); callPhone(intent);
-
官方文档 RequiresPermission
方法相关注解
-
@CallSuper
为保证父类中某个方法的逻辑被执行,可以在此父类方法中添加@CallSuper注解。这样,当子类重写此方法时,会提示子类必须调用super,使得父类方法中的逻辑确保被执行
-
@CheckResult
可被注解的方法需要有返回值,用于提示该方法的调用者注意检查其返回值,保证该方法的返回被使用
-
@VisibleForTesing
某方法需要用于测试代码中进行单元测试而声明为public,但是在主工程中却不想将其开放出来,可以使用此注解来限制主工程中此方法的访问权限
- VisibleForTesting.PRIVATE 主工程中是私有的访问权限(也是@VisibleForTesting的默认值)
- VisibleForTesting.PACKAGE_PRIVATE 包访问权限
- VisibleForTesting.PROTECTED 主工程中为protected的访问权限
- VisibleForTesting.NONE 主工程中无访问权限(即此方法仅限于测试使用)
@VisibleForTesting(otherwise = VisibleForTesting.NONE) void setSSColor(@ColorInt int color) { } setSSColor(Color.parseColor("#ff00ff"));
当方法被注解为NONE并被主工程代码所引用时,就会出现下图报错:
VisibleForTesting_NONE_lint.png
@Keep混淆注解
-
@Keep
-
在混淆时保持被注解的方法与属性不被混淆
==具体效果待验证==
@RestrictTo
-
RestrictTo.Scope.TESTS
@RestrictTo(RestrictTo.Scope.TESTS) public static void verborse(String msg){}
被注解的存在仅能被测试代码调用
-
RestrictTo.Scope.GROUP_ID
基于gradle中的group_id,属于同一个group_id下的类可以调用被注解者==具体使用待验证==
-
RestrictTo.Scope.SUBCLASS
@RestrictTo(RestrictTo.Scope.SUBCLASSES) public void getFatherName() {}
被注解的存在仅能被所在类及其子类所调用,包括测试代码以及同一包下的别类都不能使用,否则标红报错
@Dimension
待续
过滤警告注解
-
@SuppressWarnings
-
属于java.lang包中的资源,在另外一篇文章中会有阐述
Android中非Support Annotation库中注解
- ViewDebug.ExportedProperty 调试用注解
网友评论