本篇博客记录一下Android中注解的使用方式。


Android使用注解时,需要在build.gradle中导入对应的Library,例如:

dependencies {    ...........    implementation 'com.android.support:support-annotations:27.0.2'}

support-annotations中定义了一系列元注解,
用来帮助开发者在编译期间发现可能存在的Bug。

下面我们记录一下不同类型的注解。

1、Nullness注解
此类注解主要作用于函数参数或者返回值,其中:
@Nullable: 标记参数或者返回值可以为空。
@NonNull: 标记参数或者返回值不可以为空。

当出现违反注解标记的代码时,Android Studio就会给出提示。
同时,利用Android Lint进行静态代码检查时,也会有错误提示。

具体的使用示例类似于:

public void onCreate(@Nullable Bundle savedInstanceState,        @Nullable PersistableBundle persistentState) {    onCreate(savedInstanceState);}

2、资源类型注解
资源在Android中通常以整型值表示,并保存在R.java中。
这意味着一个需要传入String资源值的函数,如果传入drawable资源值,
不会出现任何编译期错误,只有在执行时才能发现问题。
此时,就可以考虑使用资源类型注解。

资源类型注解作用于函数参数、返回值及类的变量,例如:
@AnimatorRes:标记整形值为android.R.animator类型(属性动画相关)。
@AnimRes:标记整形值为android.R.anim类型(补间动画相关)。
@XmlRes:标记整形值为android.R.xml类型。
基本上所有资源类型,都有对应的注解,此处不一一列举。

具体的使用示例类似于:

@Overridepublic void setContentView(@LayoutRes int layoutResID) {    getDelegate().setContentView(layoutResID);}

3、类型定义注解
在Android开发中,整型值不止经常用来代表资源引用值,
而且经常用来代替枚举值。
@IntDef注解可用来创建一个整型类型定义的新注解,例如:

........public static final int TEST_MODE_ONE = 1;public static final int TEST_MODE_TWO = 2;//设置注解的保留策略,此处表示仅在源文件中,不编译进.class文件@Retention(RetentionPolicy.SOURCE)//定义注解可以接受的类型@IntDef({TEST_MODE_ONE, TEST_MODE_TWO})//新注解名为TestModepublic @interface TestMode {}//使用方式类似于元注解//使用该函数时,若传入0、1就会被警告,必须是TEST_MODE_ONE等private void setTestMode(@TestMode int testMode) {    ..............}..........

此外,如果需要支持多个常量的组合,需要显示将IntDef的flag设置为true,
类似于:

........public static final int TEST_MODE_ONE = 1;public static final int TEST_MODE_TWO = 2;@Retention(RetentionPolicy.SOURCE)//flag置为true时,才是使用组合值@IntDef(flag = true, value = {TEST_MODE_ONE, TEST_MODE_TWO})public @interface TestMode {}private void setTestMode(@TestMode int testMode) {    ........}................

当flag设置为true时,就可以按照下列方式调用setTestMode且不引起警告:

..........setTestMode(TEST_MODE_ONE | TEST_MODE_TWO);..........

4、线程注解
Android应用开发过程中,经常会涉及到多线程的使用,
界面相关操作必须在主线程,而耗时操作则需要放到后台线程中。
线程相关注解有四种:
@UiThread:标记运行在UI线程,对一个应用而言可能存在多个UI线程,
每个UI线程对应不同Activity的主窗口。

@MainThread:标记运行在主线程,一个应用只有一个主线程,
当然主线程也是UI线程。
通常情况下,我们使用@MainThread来注解生命周期相关的函数,
使用@UiThread来注解视图相关的函数。
一帮情况下,@MainThread和@UiThread是可以互换使用的。

@WorkThread:标记运行在后台的线程。

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

具体的使用示例类似于AsyncTask中的:

.............@MainThreadprotected void onPreExecute() {}@WorkerThreadprotected abstract Result doInBackground(Params... params);..............

5、值范围注解
当函数参数的取值是在一定范围内时,可以使用值范围注解来防止调用者传入错误的参数。
这种类型的注解有以下几种:
@Size:对于类似数组、集合和字符串之类的参数,
可以使用@Size注解来表示这些参数的大小,例如:

//@Size(min=1)表示集合的长度必须大于等于1public void setNoEmptyString(@Size(min=1) String str) {    ...........}//@Size(max=2)表示集合的长度必须小于等于2public void setLenLessThen2String(@Size(max=2) String str) {    ........}//@Size(2)表示集合的长度必须等于2public void setLenEquals2String(@Size(2) String str) {    .........}//@Size(multiple=2)表示集合的长度必须是2的倍数public void setLenMulti2String(@Size(multiple=2) String str) {    .........}

@IntRange和@FloatRange: 限定参数类型及取值范围,例如:

public void setInt(@IntRange(from=0, to=255) int value) {    .......}public void setFloat(@FloatRange(from=0.0, to=1.0) float value) {    .........        }

6、权限注解
Android应用在使用某些系统功能时,需要在AndroidManifest.xml中声明权限,
否则在运行时会提示缺失对应的权限。
为了在编译期及时发现缺失的权限,我们可以使用@RequiresPermission注解。
具体的用法类似于:

//表明调用该函数需要声明一个权限@RequiresPermission(Manifest.permission.CHANGE_NETWORK_STATE)public void setDataEnable() {    ..........}//表明调用该函数需要声明集合中至少一个权限@RequiresPermission(anyOf = {        Manifest.permission.CHANGE_NETWORK_STATE, Manifest.permission.INTERNET})public void setDataEnable() {    .........}//表明调用该函数需要声明集合中所有的权限@RequiresPermission(allOf = {        Manifest.permission.CHANGE_NETWORK_STATE, Manifest.permission.INTERNET})public void setDataEnable() {    .........}

除了修饰函数外,该注解还可以修饰Intent对应的ACTION字段或ContentProvider对应的Uri,例如:

@RequiresPermission(Manifest.permission.ACCESS_WIFI_STATE)public static final String ACTION_TEST = "com.zj.test.Wifi";//对于ContentProvider可能需要读和写这两个操作,对应不同的权限声明@RequiresPermission.Read(@RequiresPermission("read_permission"))@RequiresPermission.Write(@RequiresPermission("write_permission"))public static final Uri TEST_URI = Uri.parse("content://test/info");

7、重写函数注解
如果API允许调用者重写某个函数,但同时要求重写的函数需要调用被重写的函数,
那么可使用@CallSuper注解,例如Activity中的onCreate函数:

@MainThread@CallSuperprotected void onCreate(@Nullable Bundle savedInstanceState) {    ..........}

8、返回值注解
如果函数需要调用者对返回值做某些处理,
那么可以使用@CheckResult注解来提示开发者。

我们没必要对每个非空返回值的函数都添加这个注解,
该注解的主要目的是让调用者在使用API时,不至于怀疑该函数是否会产生副作用。
例如Bitmap.java中的:

@CheckResultpublic Bitmap extractAlpha() {    return extractAlpha(null, null);}

此外还可以指定警告信息,例如Context.java中的:

//如果调用者没有检查函数的返回值,Android Studio将会给出警告//警告信息中包含suggest指定的内容@CheckResult(suggest="#enforcePermission(String,int,int,String)")@PackageManager.PermissionResultpublic abstract int checkPermission(@NonNull String permission, int pid, int uid);

9、测试可见注解
单元测试中,可能需要访问到一些不可见的类、函数或变量,
这时可以使用@VisibleForTesting注解来使其对测试可见。

10、Keep注解
@Keep注解用来标记在Proguard混淆过程中不需要混淆的类或者方法,例如:

public class Test {    @Keep    public void test() {        ..........    }}

至此,注解相关的基本知识介绍完毕。
最后需要说明的是:
如果项目中使用了Annotation Library,并使用Gradle生成aar压缩包,
那么在编译时Android Gradle插件会抽取出注解信息并打包到aar文件中,
以便函数库的调用者正常使用注解信息。
aar包中的annotations.zip文件就是抽取出来的注解信息。

更多相关文章

  1. Android(安卓)framework Watchdog的监控过程
  2. Android(安卓)进阶16:IntentService 使用及源码解析
  3. Android学习心得(六)——位置服务
  4. Dojo mobile TweetView 系列教程之五 —— TweetView: Android,打
  5. eclipse使用appcompat_v7库无法找到android:Widget.Material.Act
  6. 箭头函数的基础使用
  7. NPM 和webpack 的基础使用
  8. Python技巧匿名函数、回调函数和高阶函数
  9. Python list sort方法的具体使用

随机推荐

  1. android中关于margin的一些注意点
  2. 关于新版SDK报错You need to use a Theme
  3. android:padding 与 android:margin的区
  4. 设置透明,模糊,黑暗度
  5. 面向忙碌开发者的 Android(安卓)知识点收
  6. (20120722)(笔记002)android开发环境搭建
  7. android登录窗口――基础编
  8. android的进度条使用
  9. 善用Android预定义样式
  10. Android下 布局加边框 指定背景色 半透明