Android进阶系列之Support Annotation Library使用详解

来源:互联网 发布:宇宙以知最大的星球 编辑:程序博客网 时间:2024/06/05 09:26

转自:http://blog.csdn.net/sw5131899/article/details/53842362

进入正题,Support Annotation Library是从Android Support Library 19.1开始引入的一个全新的函数包,它包含一系列有用的元注解,用来帮助开发者在编译期间发现可能存在的bug,Support Library本身也使用Annotation Library 提供的注解来完善自身的代码质量,Android Studio 提供可视化的交互以便开发者发现问题。


Android Support Library 发展到现在已经不止是一个jar包了,而是拆分成多个独立的Jar包,例如support-v4、support-v7、gridlayout-v7、design、cardview-v7等等。而Annotation Libary 也是其中之一,默认情况下是不会包含在工程中的,如果我们的SDK已经安装了Android Support Repository,那么我们打开Project Structure 对话框,并选中一个Module,选中Dependencies选项,点击“+”按钮,在弹出的Choose Library Dependency 对话框中轻松找到Annotation Library。



那么接下来一一对注解进项介绍。
1.Nullness
      -@Nullable作用于函数参数或返回值,标记参数或返回值为可以空。
      -@NonNull作用于函数参数或返回值,标记参数或返回值为不能为空。
如果在函数参数或返回值使用了上述注解,而又出现违反该注解的代码时,Android Studio 会给出提示,同时使用Android Lint进行静态代码扫描,也会显示出错提示。至于Android Lint使用请查看:http://blog.csdn.net/sw5131899/article/details/53908776
具体使用如下图:

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:12px;">public class MainActivity extends AppCompatActivity {  
  2.   
  3.     private static final String TAG = MainActivity.class.getName();  
  4.   
  5.     @Override  
  6.     protected void onCreate(Bundle savedInstanceState) {  
  7.         super.onCreate(savedInstanceState);  
  8.         setContentView(R.layout.activity_main);  
  9.         test(null);  
  10.     }  
  11.   
  12.     public void test(@NonNull String str){  
  13.         Log.i(TAG,str);  
  14.     }  
  15. }</span>  




在Proable bugs中,我们可以看到程序可能发生bug的地方,这对于程序猿来说是神器啊,提前预知Bug在何处,提前避开bug。
2.资源类型注解

我们知道,资源是以int整型表示,并保存在R.Java文件中。这就意味着在一个需要Layout资源值函数传入String字符串,在编译时不会报错,只有在运行时才会报错,为了防止这种情况的出现,可以使用资源类型注解。

资源类型的注解作用于函数参数、返回值及类的变量,每种资源类型对应一种注解。

AnimatorRes:标记整型值是android.R.animation类型。

AnimRes:标记整型是android.R.anim类型。

AnyRes:标记整型是任何一种资源类型,如果确切知道表示的是哪一个具体资源的话,建议显式指定。

ArrayRes:标记整型是android.R.array类型。

AttrRes:标记整型是android.R.attr类型。

BoolRes:标记整型是布尔类型。

ColorRes:标记整型是android.R.color类型。

DrawableRes:标记整型是android.R.drawable类型。

FranctionRes:标记整型值是fraction类型,这个比较少见,这种类型资源常见于Animation Xml中,比如50%,表示占parent的50%

IdRes:标记整型是android.R.id类型。

IntegerRes:标记整型是android.R.integer类型。

InterpolatorRes:标记整型是android.R.interpolator类型,插值器,在Animation Xml中使用较多。

LayoutRes:标记整型是android.R.layout类型。

MenuRes:标记整型是android.R.menu类型。

RawRes:标记整型是android.R.raw类型。

StringRes:标记整型是android.R.string类型。

StyleableRes:标记整型是android.R.styleable类型。

StyleRes:标记整型是android.R.style类型。

XmlRes:标记整型是android.R.xml类型。

看一下例子。这里传入字符串若是不加@LayoutRes,是不会报错,

若是加了就会报错。提前知道错误在哪。这样就会少很多麻烦。

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:12px;"private static final String TAG = MainActivity.class.getName();  
  2.   
  3.     @Override  
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_main);  
  7.         test(R.string.app_name);  
  8.     }  
  9.   
  10.     public void test(@LayoutRes int str){  
  11.         Log.i(TAG,""+str);  
  12.     }</span>  

3.类型定义注解

Android开发中,整型值不止经常用来代表资源引用值,而且经常用来代替枚举值。@IntDef注解用来创建一个

整型类型定义的新注解,我们可以使用这个新注解来标记自己编译的API。先看看@IntDef的源码。就明白怎么回事了。

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:12px;">@Retention(SOURCE)  
  2. @Target({ANNOTATION_TYPE})  
  3. public @interface IntDef {  
  4.     /** Defines the allowed constants for this element */  
  5.     long[] value() default {};  
  6.   
  7.     /** Defines whether the constants can be used as a flag, or just as an enum (the default) */  
  8.     boolean flag() default false;  
  9. }</span>  

这里面可以定义一个布尔值,还可以定义多个long类型的值。那么就以long数组举例。

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:12px;">public abstract class AnnotationTest {  
  2.     public static final int TEST_1 = 0;  
  3.     public static final int TEST_2 = 1;  
  4.     public static final int TEST_3 = 2;  
  5.     @Retention(RetentionPolicy.SOURCE)  
  6.     @IntDef({TEST_1,TEST_2,TEST_3})  
  7.     public @interface TestAnnotation{}  
  8.   
  9.     @TestAnnotation  
  10.     public abstract int getTestAnnotation();  
  11.   
  12.     public abstract void setTestAnnotation(@TestAnnotation int testAnnotation);  
  13. }</span>  

这里给TestAnnotation注解加上了@IntDef,这样在使用TestAnnotation的时候必须传入指定参数,若是非法在编译时就会报异常。

使用就非常简单了,找个类继承抽象类,实现方法。在调用的时候,只能传入指定的TEST_1,TEST_2,TEST_3。

这样就可以自定义资源类型注解,非常方便。

4.线程注解

Android应用开发过程中,经常会涉及到多种线程的使用,界面相关操作必须在主线程,而耗时操作如文件下载等必须在后台线程中,

线程注解相关有四种。

1.@UiThread:标记运行在UI线程,一个应用只有一个UI线程,多数是用于View的标注。(这里先前有错误,感谢楼下评论朋友提醒)

2.@MainThread:标记运行在主线程,一个应用只有一个主线程,主线程也是@UiThread线程。通常情况下,我们使用@MainThread

来注解生命周期相关函数,使用@UiThread来注解视图相关函数,一般情况下@MianThread和@UiThraed是可以互换的。

3.@WorkerThread:标记运行在后台运行线程。

4.@BinderThread:标记运行在Binder线程。

看个简单的例子。

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:12px;">public void threadtest(){  
  2.         new Thread(new TimerTask() {  
  3.             @Override  
  4.             public void run() {  
  5.                 setTest();  
  6.             }  
  7.         }).start();  
  8.     }  
  9.   
  10.     @UiThread  
  11.     public void setTest(){  
  12.         test.setText("测试");  
  13.     }</span>  

创建一个新线程,在子线程中调用TextView的setTest方法,大家都知道改变视图只能在UI主线程中。

那么只要加上注解,在子线程调用效果是一样的。不会报错。(这里有错误,感谢,流浪的猫1712,这位朋友指出,这里的注解是不能切换线程的,我也去官网看了看文档,确实只是起到一个提示作用,在编译时会给出提示,在生成的注解在字节码中。这里对大家造成误解表示抱歉。https://developer.android.com/reference/android/support/annotation/AnimatorRes.html

5.RGB颜色值注解

在资源类型注解中我们使用@ColorRes来标记参数类型需要传入颜色类型的id,而使用@ColorInt注解是标记参数类型需要传入RGB

或者ARGB颜色值的整型值。

在TextView的源码中可以找到如下例子。

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:12px;">public void setTestColor(@ColorInt int color){  
  2.         mTextColor = ColorStateList.valueOf(color);  
  3.         updateTextViewColors();  
  4.     }</span>  
6.值范围注解

当函数参数的取值在一定范围时,可以使用注解来防止调用者传入错误的参数,主要注解有三种。

1.@Size:对于类似数组、集合和字符串之类的参数,我们可以使用@Size注解来表示这些参数的大小。用法:

@Size(min=1)//可以表示集合不可以为空

@Size(max=23)//可以表示字符串最大字符个数为23

@Size(2)//表示数组元素个数为2个

@Size(multiple=2)//可以表示数组大小是2的倍数

2.@IntRange:参数类型是int或者long,用法如下

public void setInt(@intRange(from=0,to=255)){...}

3.@FloatRange:参数类型是float或者double,用法如下。

public void setFloat(@FloatRange(from=0.0,to=1.0)){...}

7.权限注解

Android应用在使用某些系统功能时,需要在AndroidManifest,xml中声明权限,否则在运行时就会提示缺失对应的权限,

为了在编译时及时发现权限的缺失,我们可以使用@RequiresPermission注解。

1.如果需要一个权限则加注解。@RequiresPermission(Manifest.permission.SET_WALLPAPER)

2.如果需要一个集合至少一个权限,那么就加注解。@RequiresPermission(anyOf = {Manifest.permission.SET_WALLPAPER,

Manifest.permission.CAMERA})

3.如果同时需要多个权限,那么就加注解。@RequiresPermission(allOf = {Manifest.permission.SET_WALLPAPER,Manifest.

permission.CAMERA})

4.对于Intent调用所需权限的ACTION字符串定义处添加注解。

@RequiresPermission(android.Manifest.permission.BLUETOOTH)

String ACTION_REQUEST_DISCOVERRAVLE = "android.bluetooth.adapter.REQUEST_DISCOVERRAVLE";

5.对于ContentProvider所需权限,可能有读和写两个操作。对应不同的权限。

@RequiresPermission.Read(@RequestPermission(READ_HISTORY_BOOLMARKS))

@RequiresPermission.Write(@RequestPermission(WRITE_HISTORY_BOOLMARKS))

public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks);

8.重写函数注解

如果API允许重写某个函数,但是要求在重写该函数时需要调用super父类的函数。

可以加注解@CallSuper来提示开发者。testCallSuper方法我加了注解,若是重写不调用super就会报错。

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:12px;">public abstract class AnnotationTest {  
  2.     public static final int TEST_1 = 0;  
  3.     public static final int TEST_2 = 1;  
  4.     public static final int TEST_3 = 2;  
  5.     @Retention(RetentionPolicy.SOURCE)  
  6.     @IntDef( flag = false,value = {TEST_1,TEST_2,TEST_3})  
  7.     public @interface TestAnnotation{}  
  8.   
  9.     @TestAnnotation  
  10.     public abstract int getTestAnnotation();  
  11.   
  12.     public abstract void setTestAnnotation(@TestAnnotation int testAnnotation);  
  13.       
  14.     @CallSuper  
  15.     public void testCallSuper(){  
  16.         Log.i("tag00","必须调用父类的super");  
  17.     }  
  18. }</span>  
[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:12px;">public class AnnotationObject extends AnnotationTest {  
  2.     private int test;  
  3.   
  4.     @Override  
  5.     public int getTestAnnotation() {  
  6.         return test;  
  7.     }  
  8.   
  9.     @Override  
  10.     public void setTestAnnotation(@TestAnnotation int testAnnotation) {  
  11.         test = testAnnotation;  
  12.     }  
  13.   
  14.     @Override  
  15.     public void testCallSuper() {  
  16.         super.testCallSuper();  
  17.     }  
  18. }</span>  
子类在重写testCallSuper方法时,必须调用super.testCallSuper();

9.@Keep注解

@keep是用来标记在Proguard混淆过程中不需要混淆的类或者方法。在混淆时一些不需要混淆的会使用

-keep class com.foo.bar{public static <method>}

有了@Keep之后,就可以在编码时标注出一些不需要混淆的类或者方法、

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:12px;">public abstract class AnnotationTest {  
  2.     public static final int TEST_1 = 0;  
  3.     public static final int TEST_2 = 1;  
  4.     public static final int TEST_3 = 2;  
  5.     @Keep  
  6.     @Retention(RetentionPolicy.SOURCE)  
  7.     @IntDef( flag = false,value = {TEST_1,TEST_2,TEST_3})  
  8.     public @interface TestAnnotation{}  
  9.   
  10.     @TestAnnotation  
  11.     public abstract int getTestAnnotation();  
  12.   
  13.     public abstract void setTestAnnotation(@TestAnnotation int testAnnotation);  
  14.   
  15.     @CallSuper  
  16.     public void testCallSuper(){  
  17.         Log.i("tag00","必须调用父类的super");  
  18.     }  
  19. }</span>  
TestAnnotation在混淆代码时就会被忽略。

10.@SuppressWarnings注解

这个注解在源码里是随处可见,其实它的用法很简单,就是对一些警告信息的过滤。那么怎么使用呢,我们来看看它的源码。

[java] view plain copy print?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:12px;">@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE})  
  2. @Retention(RetentionPolicy.SOURCE)  
  3. public @interface SuppressWarnings {  
  4.     String[] value();  
  5. }</span>  

这个注解是可以使用在属性、方法、构造方法、变量等等。那么它的参数就是一个字符串数组。可以单个,可以多个。

那么字符串有哪些呢。根据sun的官方文档描述:

value - 将由编译器在注释的元素中取消显示的警告集。允许使用重复的名称。忽略第二个和后面出现的名称。出现未被识别的警告名不是 错误:编译器必须忽略无法识别的所有警告名。但如果某个注释包含未被识别的警告名,那么编译器可以随意发出一个警告。各编译器供应商应该将它们所支持的警告名连同注释类型一起记录。鼓励各供应商之间相互合作,确保在多个编译器中使用相同的名称。

示例:
·   @SuppressWarnings("unchecked")
告诉编译器忽略 unchecked 警告信息,如使用List,ArrayList等未进行参数化产生的警告信息。
·   @SuppressWarnings("serial")
如果编译器出现这样的警告信息:The serializable class WmailCalendar does not declare a static final serialVersionUID field of type long
       使用这个注释将警告信息去掉。
·   @SuppressWarnings("deprecation")
如果使用了使用@Deprecated注释的方法,编译器将出现警告信息。
       使用这个注释将警告信息去掉。
·   @SuppressWarnings("unchecked", "deprecation")
告诉编译器同时忽略unchecked和deprecation的警告信息。
·   @SuppressWarnings(value={"unchecked", "deprecation"})
等同于@SuppressWarnings("unchecked", "deprecation")

到这里注解玩的差不多了。那么就开始运用到项目中提高自己编码的质量吧。
0 0