美文网首页AndroidWorldAndroid 开发相关文章收集android开发
还在用枚举?我早就抛弃了!(Android 注解详解)

还在用枚举?我早就抛弃了!(Android 注解详解)

作者: OCNYang | 来源:发表于2016-12-28 15:27 被阅读7897次

还在用枚举?我早就抛弃了!(Android 注解详解)

</br>

enum

前言:使用Support Annotations注解优化代码

本片文章讲解怎么使用Support Annotations注解优化代码,比如使用 android特有的魔术变量注解替代Enum 等功能。不要看见使用注解就想到反射会影响性能之类,今天我们就来学习一下Android Support Annotations注解来优化我们的代码,增加可读性的同时,也让让更多的错误消灭在萌芽之中。

Support Annotations 简介:

Android support library 不断地引入新的注解库,它包含很多有用的元注解,你能用它们修饰你的代码,帮助你发现bugSupport library 自己本身也用到了这些注解,所以作为 support library 的用户,Android Studio已经基于这些注解校验了你的代码并且标注其中潜在的问题。

Support Annotations 如何引入:

注解默认是没有包含的;它被包装成一个独立的库,如果使用了appcompat库,那么 Support Annotations 就会自动引入进来,因为 appcompat 使用了 Support Annotations,如果没有则需要在 build.gradle 中添加如下配置:

dependencies {
    compile 'com.android.support:support-annotations:23.4.0'
}

如果你已经引入了 appcompat v7 则可能就没有必要再次引用 support-annotations ,因为 appcompat 默认包含了对其引用。

Support Annotations分类:

Typedef 注解:IntDef / StringDef (Android特有的魔术变量注解替代Enum)

Enum in Android 枚举Enum在java中是一个完整的类. 而枚举中的每一个值在枚举类中都是一个对象. 所以在我们使用时枚举的值将比整数常量消耗更多的内存. 当我们使用枚举在安卓应用中, 如果我们的程序本身内存消耗就比较大,或者是一个游戏的应用程序. 那么我们最好使用常量来替代枚举。可是使用了常量代替后又不能限制取值了。那有什么好的办法呢?

当然, Android 支持注解库中有一些好用的annotation helper 我们可以使用它们来解决我们之前的问题(在编译代码时期).
IntDef和StringDef 是两个魔术变量注解. 使用这个两个来替代之前使用的Enum. 它将帮助我们在编译代码时期像Enum那样选择变量的功能。 @IntDef和”typedef”作用非常类似,你可以创建另外一个注解,然后用@IntDef指定一个你期望的整型常量值列表,最后你就可以用这个定义好的注解修饰你的API了。接下来我们来使用IntDef来替换Enum看一下.

public class MainActivity extends Activity {
    public static final int SUNDAY = 0;
    public static final int MONDAY = 1;
    {...省略部分}

    @IntDef({SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY})
    @Retention(RetentionPolicy.SOURCE)
    public @interface WeekDays {
    }

    @WeekDays
    int currentDay = SUNDAY;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        setCurrentDay(WEDNESDAY);

        @WeekDays int today = getCurrentDay();
        switch (today) {
            case SUNDAY:
                break;
            case MONDAY:
                break;
            {...省略部分}
            default:
                break;
        }
    }
    
    /**
     * 参数只能传入在声明范围内的整型,不然编译通不过
     * @param currentDay
     */
    public void setCurrentDay(@WeekDays int currentDay) {
        this.currentDay = currentDay;
    }

    @WeekDays
    public int getCurrentDay() {
        return currentDay;
    }
}

说明:

  1. 声明一些必要的 int 常量
  2. 声明一个注解为 WeekDays
  3. 使用 @IntDef 修饰 WeekDays,参数设置为待枚举的集合
  4. 使用 @Retention(RetentionPolicy.SOURCE) 指定注解仅存在与源码中,不加入到 class 文件中

需要在调用时只能传入指定类型,如果传入类型不对,编译不通过。

我们也可以指定整型值作为标志位,也就是说这些整型值可以使用 ’|’ 或者 ’&’ 进行与或等操作。如果我们把@Flavour定义为如下标志位:

@IntDef(flag = true, value = {SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY})
public @interface Flavour {
}

那么可以如下调用:

setCurrentDay(SUNDAY & WEDNESDAY);

@StringDef用法和@IntDef基本差不多,只不过是针对String类型而已。

怎么样?使用起来还是很简单的吧。如果你只是冲做标题 寻求一种替代Enum的方式 进来的话,看到这你就可以直接跳到文章结尾 点个喜欢 离开了。如果你还想知道更多关于注解的强大功能,那么就接着看下面的内容吧!

Nullness注解

@Nullable注解可以用来标识特定的参数或者返回值可以为null。

@Nullable

@NonNull注解可以用来标识参数不能为null。

@NonNull

Resource Type 注解

资源在Android中作为整型值来传递。这意味着希望获取一个drawable作为参数的代码很容易被传递了一个string类型的资源,因为他们资源id都是整型的,编译器很难区分。Resource Type注解在这种条件下可以提供类型检查,例如:

如果类型指定错误,编译不会通过。

Resource Type

常见的Resource Type注解,使用方式都是指定一个integer的参数、成员变量、或者方法,检查对应的资源类型。

  • AnimatorRes :animator资源类型
  • AnimRes:anim资源类型
  • AnyRes:任意资源类型
  • ArrayRes:array资源类型
  • AttrRes:attr资源类型
  • BoolRes:boolean资源类型
  • ColorRes:color资源类型
  • DimenRes:dimen资源类型。
  • DrawableRes:drawable资源类型。
  • FractionRes:fraction资源类型
  • IdRes:id资源类型
  • IntegerRes:integer资源类型
  • InterpolatorRes:interpolator资源类型
  • LayoutRes:layout资源类型
  • MenuRes:menu资源类型
  • PluralsRes:plurals资源类型
  • RawRes:raw资源类型
  • StringRes:string资源类型
  • StyleableRes:styleable资源类型
  • StyleRes:style资源类型
  • TransitionRes:transition资源类型
  • XmlRes:xml资源类型

以上基本上包括了所有的资源类型,但是有时需要通过RGB颜色整型来设置颜色值,在这种情况下,你可以使用 @ColorInt 注解,表示你期望的是一个代表颜色的整数值,如果使用不对同样也是编译不通过

@ColorInt

Threading 注解

比如我们在项目中处理比较耗时的操作,需要制定在工作子线程中执行,可以使用Threading 注解,如果没有在制定的线程中执行也是编译不过的

Threading

几种Threading注解

  • @UiThread UI线程
  • @MainThread 主线程
  • @WorkerThread 子线程
  • @BinderThread 绑定线程

Value Constraints 注解:@Size, @IntRange, @FloatRange

在实际开发过程中,我们有时可能需要设置一个取值范围,这时我们可以使用取值范围注解来约束。
比如我们设置一个百分比,取值范围为0-100,

@IntRange

对于数据、集合以及字符串,你可以用@Size注解参数来限定集合的大小(当参数是字符串的时候,可以限定字符串的长度)。

举几个例子

  • 集合不能为空: @Size(min=1)
  • 字符串最大只能有23个字符: @Size(max=23)
  • 数组只能有2个元素: @Size(2)
  • 数组的大小必须是2的倍数 (例如图形API中获取位置的x/y坐标数组: @Size(multiple=2)

Permissions 注解: @RequiresPermission

有时我们的方法调用需要调用者拥有指定的权限,这时我们可以使用@RequiresPermission注解,

@RequiresPermission(Manifest.permission.SET_WALLPAPER)
public abstract void setWallpaper(Bitmap bitmap) throws IOException;

除了上面的单一使用方式,官方同时也给出了如下几种使用场景

(1)如果你至少需要权限集合中的一个,你可以使用anyOf属性

@RequiresPermission(anyOf = {
    Manifest.permission.ACCESS_COARSE_LOCATION,
    Manifest.permission.ACCESS_FINE_LOCATION})
public abstract Location getLastKnownLocation(String provider);

(2)如果你同时需要多个权限,你可以用allOf属性

@RequiresPermission(allOf = {
    Manifest.permission.READ_HISTORY_BOOKMARKS, 
    Manifest.permission.WRITE_HISTORY_BOOKMARKS})
public static final void updateVisitedHistory(ContentResolver cr, String url, boolean real) ;  

(3)对于intents的权限,可以直接在定义的intent常量字符串字段上标注权限需求(他们通常都已经被@SdkConstant注解标注过了)

@RequiresPermission(android.Manifest.permission.BLUETOOTH)
public static final String ACTION_REQUEST_DISCOVERABLE =
            "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";

(4)对于content providers的权限,你可能需要单独的标注读和写的权限访问,所以可以用@Read或者@Write标注每一个权限需求

@RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
@RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");  

Overriding Methods 注解: @CallSuper

如果你的API允许使用者重写你的方法,但是呢,你又需要你自己的方法(父方法)在重写的时候也被调用,这时候你可以使用@CallSuper标注

例如:Activity的onCreate函数

@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) 

用了这个后,当重写的方法没有调用父方法时,工具就会给予标记提示

@CallSuper

Return Values注解: @CheckResult

如果你的方法返回一个值,你期望调用者用这个值做些事情,那么你可以使用 @CheckResult 注解标注这个方法。

这个在具体使用中用的比较少,除非特殊情况,比如在项目中对一个数据进行处理,这个处理比较耗时,我们希望调用该函数的调用者在不需要处理结果的时候,提示没有使用,酌情删除调用。

@CheckResult

Keep 注解

Keep:指出一个方法在被混淆的时候应该被保留。

在Android编译生成APK的环节,我们通常需要设置minifyEnabled为true实现下面的两个效果:

  • 混淆代码
  • 删除没有用的代码

但是出于某一些目的,我们需要不混淆某部分代码或者不删除某处代码,除了配置复杂的Proguard文件之外,我们还可以使用@Keep注解.

@Keep
public static int getBitmapWidth(Bitmap bitmap) {
    return bitmap.getWidth();
}

其他注解

VisibleForTesting:可注解一个类,方法,或变量,表示有更宽松的可见性,这样它能够有更宽泛的可见性,使代码可以被测试。

IntelliJ注解

IntelliJ ,Android Studio 就是基于它开发的, IntelliJ 有一套它自己的注解; IntDef 分析其实重用的是 MagicConstant 分析的代码,IntelliJ null 分析其实用的是一组配置好的 null 注解。如果你执行 Analyze > Infer Nullity…,它会试图找出所有的 null 约束并添加他们。这个检查有时会插入 IntelliJ 注解。你可以通过搜索,替换为 Android 注解库的注解,或者你也可以直接用 IntelliJ 注解。在 build.gradle 里或者通过 Project Structure 对话框的 Dependencies 面板都可以添加如下依赖:

dependencies {
    compile 'com.intellij:annotations:12.0'
}

结尾:

经过查阅资料和博客,系统了学习和总结了关于Support Annotations注解的内容,在编码中通过使用Support Annotations可以提高代码可读性,同时可以在类加载时就可以检查一些错误,同时不会对性能有任何影响,因为Support Annotations中的注解的生命周期全部是RetentionPolicy.class。在以后的编码中大家可以尝试用用。

参考文章(本编文章内容大多来自对下面几篇文章的总结)

http://droidyue.com/blog/2016/08/14/android-annnotation/index.html
http://www.cnblogs.com/whoislcj/p/5677917.html
https://asce1885.gitbooks.io/android-rd-senior-advanced/content/shen_ru_qian_chu_android_support_annotations.html
https://noobcoderblog.wordpress.com/2015/04/12/java-enum-and-android-intdefstringdef-annotation/
http://szysky.com/2016/05/20/Android-%E4%B8%AD%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8annotion%E6%9B%BF%E4%BB%A3Enum/
http://anupcowkur.com/posts/a-look-at-android-support-annotations/

相关文章

  • 还在用枚举?我早就抛弃了!(Android 注解详解)

    还在用枚举?我早就抛弃了!(Android 注解详解) 前言:使用Support Annotations注解优化代...

  • Android 编译时注解 —— 语法详解

    java Type 详解 java 反射机制详解 注解使用入门(一) Android 自定义编译时注解1 - 简单...

  • Android注解全面解析

    一、注解知识体系 二、基础知识 必备基础知识:了解注解 三、注解在Android中的应用 1. 利用注解代替枚举 ...

  • Android中使用注解替代枚举

    枚举最大的作用是提供了类型安全。为了弥补Android平台不建议使用枚举的缺陷,官方推出了两个注解,IntDef和...

  • Android中使用注解替代枚举

    枚举最大的作用是提供了类型安全。为了弥补Android平台不建议使用枚举的缺陷,官方推出了两个注解,IntDef和...

  • 枚举 学习笔记

    android 中 我们通常推荐使用 @IntDef 等 代替使用 枚举,很多同学有疑问,为什么抛弃 枚举。我们现...

  • Dagger2入门详解

    Dagger2入门详解 @(Android) Dagger2入门详解 参考文章 环境配置 入门实例 其他注解和情况...

  • Java注解

    Java注解(Annotation)详解(一)——概述及JDK自带注解 Java注解(Annotation)详解(...

  • 性能

    @IntDef替代枚举 @IntDef/@StringDef本身是个Android中提供的一种注解. 添加依赖:c...

  • 替代枚举的注解

    Android中新引入的替代枚举的注解有IntDef和StringDef,这里以IntDef做例子说明一下.

网友评论

  • OCNYang:🙃
    OCNYang:@OCNYang :eyes:
    OCNYang: @OCNYang 🐔
  • 黑马有点白986:上面讨论得替代方案还是存在问题的,因为你的注解类是写在当前类中,但是很多时候,服务端下行的类型不止一个类中使用,需要我们做出类型抽取,这样,你的set方法就存在问题。
  • 黑马有点白986:谢谢分享
  • Jepack:这个keep 注解很有用
    OCNYang:@breakingbad The @Keep annotation ensures that an annotated element is not removed when the code is minified at build time. It is typically added to methods and classes that are accessed via reflection to prevent the compiler from thinking that the code is unused.

    从上面官方Api文档中的可以看出:“methods and classes”可以修饰在方法和类前,所以应用于类应该是有效的。
    breakingbad:@OCNYang keep注解不能应用于类?
    OCNYang: @五叶大师 用到时用起来都很方便的。
  • Jepack:不错,谢谢分享
    OCNYang: @五叶大师 🙃

本文标题:还在用枚举?我早就抛弃了!(Android 注解详解)

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