Android进阶Support Annotation Library 使用详解
2017-09-04 00:00
204 查看
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。
点击添加后,在Module的build.gradle文件中会新增加Annotation函数库的依赖如下:
下面我们按照类型进行一一介绍。
@Nullable作用于函数参数或返回值,标记参数或返回值可以为空。
@NonNull作用于函数参数或返回值,标记参数或返回值不能为为空。
如果在函数参数或返回值使用了上述注解,而又出现违反该注解的代码时,Android Studio 会给出提示,同时使用Android Lint进行静态代码扫描,也会显示出错提示。
我们用Android Lint扫描这个文件的话,在扫描结果中会出现如下错误:
资源类型的注解作用于函数参数、返回值及类的变量,每种资源类型对应一种注解。
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,是不会报错,
若是加了就会报错。提前知道错误在哪。这样就会少很多麻烦。举个例子,在AppCompatAcitivity的setContentView函数使用@LayoutRes标记它的参数。
这里面可以定义一个布尔值,还可以定义多个long类型的值。那么就以long数组举例。
这里给TestAnnotation注解加上了@IntDef,这样在使用TestAnnotation的时候必须传入指定参数,若是非法在编译时就会报异常。
使用就非常简单了,找个类继承抽象类,实现方法。在调用的时候,只能传入指定的TEST_1,TEST_2,TEST_3。
这样就可以自定义资源类型注解,非常方便。
线程注解相关有四种。
@UiThread:标记运行在UI线程,一个应用只有一个UI线程,多数是用于View的标注。(这里先前有错误,感谢楼下评论朋友提醒)
@MainThread:标记运行在主线程,一个应用只有一个主线程,主线程也是@UiThread线程。通常情况下,我们使用@MainThread
来注解生命周期相关函数,使用@UiThread来注解视图相关函数,一般情况下@MianThread和@UiThraed是可以互换的。
@WorkerThread:标记运行在后台运行线程。
@BinderThread:标记运行在Binder线程。
一个典型的例子就是AsyncTask的源码,我们截取部分代码如下。
@Size:对于类似数组、集合和字符串之类的参数,我们可以使用@Size注解来表示这些参数的大小。用法:
@Size(min=1)//可以表示集合不可以为空
@Size(max=23)//可以表示字符串最大字符个数为23
@Size(2)//表示数组元素个数为2个
@Size(multiple=2)//可以表示数组大小是2的倍数
@IntRange:参数类型是int或者long,用法如下
@FloatRange:参数类型是float或者double,用法如下。
如果函数调用需要声明一个权限,语句如下:
如果需要一个集合至少一个权限,语句如下:
如果同时需要多个权限,语句如下。
对于Intent调用所需权限,可以在Intent的ACTION字符串定义处添加注解。语句如下:
对于ContentProvider所需权限,可能有读和写两个操作。对应不同的权限。
如果调用者没有检查这两个函数的返回值,那么就会警告,警告信息中包含suggest属性中的内容。
有了@Keep之后,就可以在编码时标注出一些不需要混淆的类或者方法
注意:SuppressWarnings是java.lang包下的,之前说的都是android.support.annotation包下的
使用:
@SuppressWarnings(“”)
@SuppressWarnings({})
@SuppressWarnings(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”)
1、抑制单类型警告
2、抑制多类型警告
3、抑制全部警告
注解目标
通过 @SuppressWarnings 的源码可知,其注解目标为类、字段、函数、函数入参、构造函数和函数的局部变量。而家建议注解应声明在最接近警告发生的位置。
到这里基本注解都已经说完了,最后说明一下,如果函数库中使用Annotation Library,并使用Gradle生成aar压缩包,那么在编译时Android Gradle插件会取出这些注解信息并打包在aar文件中,以便函数库的调用者正常使用我们的注解信息。aar文件中的annotations.zip文件就是抽取出来的注解信息。
参考:
@SuppressWarnings忽略警告
《Android高级进阶》
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。
点击添加后,在Module的build.gradle文件中会新增加Annotation函数库的依赖如下:
compile 'com.android.support:support-annotations:26.0.0-alpha1'
下面我们按照类型进行一一介绍。
1. Nullness 注解
此类型包含如下内容:@Nullable作用于函数参数或返回值,标记参数或返回值可以为空。
@NonNull作用于函数参数或返回值,标记参数或返回值不能为为空。
如果在函数参数或返回值使用了上述注解,而又出现违反该注解的代码时,Android Studio 会给出提示,同时使用Android Lint进行静态代码扫描,也会显示出错提示。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); test(null); } private void test(@NonNull String str) { Log.e(TAG,str); }
我们用Android Lint扫描这个文件的话,在扫描结果中会出现如下错误:
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,是不会报错,
若是加了就会报错。提前知道错误在哪。这样就会少很多麻烦。举个例子,在AppCompatAcitivity的setContentView函数使用@LayoutRes标记它的参数。
@Override public void setContentView(@LayoutRes int layoutResID) { getDelegate().setContentView(layoutResID); }
3. 类型定义注解
在Android开发中,整型值不止经常用来代表资源引用值,而且经常用来代替枚举值。@IntDef注解用来创建一个整型类型定义的新注解,我们可以使用这个新注解来标记自己编译的API。先看看@IntDef的源码。@Retention(SOURCE) @Target({ANNOTATION_TYPE}) public @interface IntDef { /** Defines the allowed constants for this element */ long[] value() default {}; /** Defines whether the constants can be used as a flag, or just as an enum (the default) */ boolean flag() default false; }
这里面可以定义一个布尔值,还可以定义多个long类型的值。那么就以long数组举例。
public abstract class AnnotationTest { public static final int TEST_1 = 0; public static final int TEST_2 = 1; public static final int TEST_3 = 2; @Retention(RetentionPolicy.SOURCE) @IntDef({TEST_1,TEST_2,TEST_3}) public @interface TestAnnotation{} @TestAnnotation public abstract int getTestAnnotation(); public abstract void setTestAnnotation(@TestAnnotation int testAnnotation); }
这里给TestAnnotation注解加上了@IntDef,这样在使用TestAnnotation的时候必须传入指定参数,若是非法在编译时就会报异常。
使用就非常简单了,找个类继承抽象类,实现方法。在调用的时候,只能传入指定的TEST_1,TEST_2,TEST_3。
这样就可以自定义资源类型注解,非常方便。
4. 线程注解
Android应用开发过程中,经常会涉及到多种线程的使用,界面相关操作必须在主线程,而耗时操作如文件下载等必须在后台线程中,线程注解相关有四种。
@UiThread:标记运行在UI线程,一个应用只有一个UI线程,多数是用于View的标注。(这里先前有错误,感谢楼下评论朋友提醒)
@MainThread:标记运行在主线程,一个应用只有一个主线程,主线程也是@UiThread线程。通常情况下,我们使用@MainThread
来注解生命周期相关函数,使用@UiThread来注解视图相关函数,一般情况下@MianThread和@UiThraed是可以互换的。
@WorkerThread:标记运行在后台运行线程。
@BinderThread:标记运行在Binder线程。
一个典型的例子就是AsyncTask的源码,我们截取部分代码如下。
@MainThread protected void onPreExecute() { } @MainThread protected void onPostExecute(Result result) { } @MainThread protected void onProgressUpdate(Progress... values) { }
5. RGB颜色值注解
在资源类型注解中我们使用@ColorRes来标记参数类型需要传入颜色类型的id,而使用@ColorInt注解是标记参数类型需要传入RGB或者ARGB颜色值的整型值,在TextView的源码中可以找到@ColorInt的例子。@android.view.RemotableViewMethod public void setTextColor(@ColorInt int color) { mTextColor = ColorStateList.valueOf(color); updateTextColors(); }
6. 值范围注解
当函数参数的取值在一定范围时,可以使用注解来防止调用者传入错误的参数,主要注解有三种注解。@Size:对于类似数组、集合和字符串之类的参数,我们可以使用@Size注解来表示这些参数的大小。用法:
@Size(min=1)//可以表示集合不可以为空
@Size(max=23)//可以表示字符串最大字符个数为23
@Size(2)//表示数组元素个数为2个
@Size(multiple=2)//可以表示数组大小是2的倍数
@IntRange:参数类型是int或者long,用法如下
public void setAlpha(@IntRange(from=0,to=255) int alpha){...}
@FloatRange:参数类型是float或者double,用法如下。
public void setAlpha(@FloatRange(from=0.0,to=1.0) float alpha){...}
7. 权限注解
Android应用在使用某些系统功能时,需要在AndroidManifest,xml中声明权限,否则在运行时就会提示缺失对应的权限,为了在编译时及时发现权限的缺失,我们可以使用@RequiresPermission注解。如果函数调用需要声明一个权限,语句如下:
@RequiresPermission(Manifest.permission.SET_WALLPAPER) public abstract void setWallpaper(Bitmap bitmap) throws IOException;
如果需要一个集合至少一个权限,语句如下:
@RequiresPermission(anyOf= { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}) public abstract Location getLastKnownLocation(String Provider);
如果同时需要多个权限,语句如下。
@RequiresPermission(allOf = { Manifest.permission.READ_HISTORY_BOOKMARKS, Manifest.permission.WRITE_HISTORY_BOOKMARKS}) public static final void updateVisitedHistory(ContentResolver cr,String url,boolean real);
对于Intent调用所需权限,可以在Intent的ACTION字符串定义处添加注解。语句如下:
@RequiresPermission(android.Manifest.permission.BLUETOOTH) public static final String ACTION_REQUEST_DISCOVERRAVLE = "android.bluetooth.adapter.REQUEST_DISCOVERRAVLE";
对于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来提示开发者。@CallSuper protected void onCreate(@Nullable Bundle saveInstanceState);
9. 返回值注解
如果我们编写的函数需要调用对返回值做某种处理,那么可以使用@CheckResult注解来提示开发者。当然我们没有必要对每个非空返回值的函数都添加这个注解,该注解的主要目的是让调用者在使用API时不至于怀疑该函数是否会产生副作用。看下Context类中checkPermission中的源码:@CheckResult(suggest="#enforcePermission(String,int,int,String)") @PackageManager.PermissionResult public abstract int checkPermission(@NonNull String permission, int pid, int uid); @CheckResult(suggest="#enforceCallingPermission(String,String)") @PackageManager.PermissionResult public abstract int checkCallingPermission(@NonNull String permission);
如果调用者没有检查这两个函数的返回值,那么就会警告,警告信息中包含suggest属性中的内容。
10. @VisibleForTesting注解
单元测试中可能需要访问到一些不可见的类、函数或者变量,这时可以使用@VisibleForTesting注解来使其对测试可见。11. @Keep注解
@keep是用来标记在Proguard混淆过程中不需要混淆的类或者方法。在混淆时一些不需要混淆的会使用-keep class com.foo.bar{public static method>}
有了@Keep之后,就可以在编码时标注出一些不需要混淆的类或者方法
public class AnnotationDemo { @Keep public void doSomething(){ // ... } // ... }
12. @SuppressWarnings忽略警告
java.lang.SuppressWarnings是J2SE 5.0中标准的Annotation之一。可以标注在类、字段、方法、参数、构造方法,以及局部变量上。作用:告诉编译器忽略指定的警告,不用在编译完成后出现警告信息。注意:SuppressWarnings是java.lang包下的,之前说的都是android.support.annotation包下的
使用:
@SuppressWarnings(“”)
@SuppressWarnings({})
@SuppressWarnings(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”)
1、抑制单类型警告
@SuppressWarnings("unchecked") public void addItems(String item){ @SuppressWarnings("rawtypes") List items = new ArrayList(); items.add(item); }
2、抑制多类型警告
@SuppressWarnings(value={"unchecked", "rawtypes"}) public void addItems(String item){ List items = new ArrayList(); items.add(item); }
3、抑制全部警告
@SuppressWarnings("all") public void addItems(String item){ List items = new ArrayList(); items.add(item); }
注解目标
通过 @SuppressWarnings 的源码可知,其注解目标为类、字段、函数、函数入参、构造函数和函数的局部变量。而家建议注解应声明在最接近警告发生的位置。
到这里基本注解都已经说完了,最后说明一下,如果函数库中使用Annotation Library,并使用Gradle生成aar压缩包,那么在编译时Android Gradle插件会取出这些注解信息并打包在aar文件中,以便函数库的调用者正常使用我们的注解信息。aar文件中的annotations.zip文件就是抽取出来的注解信息。
参考:
@SuppressWarnings忽略警告
《Android高级进阶》
相关文章推荐
- Android进阶系列之Support Annotation Library使用详解
- Android进阶系列之Support Annotation Library使用详解
- Android进阶系列--Design Support Library使用详解(Snackbar,TextInputLayout,TabLayout,NavigationView...)
- Android进阶系列--Design Support Library使用详解(Snackbar,TextInputLayout,TabLayout,NavigationView...)
- Android Design Support Library使用详解
- Android Design Support Library 兼容库使用详解
- Android Design Support Library使用详解
- Android Design Support Library使用详解——Snackbar
- Support Annotation Library 使用详解
- Android Design Support Library使用详解——TextInputLayout与TextInputEditText
- Android Design Support Library使用详解
- Support Annotation Library使用详解
- Android Design Support Library使用详解
- Support Annotation Library使用详解
- Android Support Annotation Library 使用
- Android Design Support Library使用详解——Snackbar
- Android Design Support Library使用详解
- Android Design Support Library使用详解
- Android Design Support Library使用详解
- Android Design Support Library使用详解