Support Annotation Library

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(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_Intent_lint.png
  • 官方文档 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 调试用注解

参考

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容