美文网首页
Support Annotation Library

Support Annotation Library

作者: 钦_79f7 | 来源:发表于2019-12-19 12:47 被阅读0次

    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 调试用注解

    参考

    相关文章

      网友评论

          本文标题:Support Annotation Library

          本文链接:https://www.haomeiwen.com/subject/blemnctx.html