Android中的注解

Posted ZhangJianIsAStark

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android中的注解相关的知识,希望对你有一定的参考价值。

本篇博客记录一下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类型。
基本上所有资源类型,都有对应的注解,此处不一一列举。

具体的使用示例类似于:

@Override
public 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)
//新注解名为TestMode
public @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中的:

.............
@MainThread
protected void onPreExecute() 


@WorkerThread
protected abstract Result doInBackground(Params... params);
..............

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

//@Size(min=1)表示集合的长度必须大于等于1
public void setNoEmptyString(@Size(min=1) String str) 
    ...........


//@Size(max=2)表示集合的长度必须小于等于2
public void setLenLessThen2String(@Size(max=2) String str) 
    ........


//@Size(2)表示集合的长度必须等于2
public 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
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) 
    ..........

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

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

@CheckResult
public Bitmap extractAlpha() 
    return extractAlpha(null, null);

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

//如果调用者没有检查函数的返回值,Android Studio将会给出警告
//警告信息中包含suggest指定的内容
@CheckResult(suggest="#enforcePermission(String,int,int,String)")
@PackageManager.PermissionResult
public 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文件就是抽取出来的注解信息。

以上是关于Android中的注解的主要内容,如果未能解决你的问题,请参考以下文章

Android 组件化路由组件 ( 构造路由表中的路由信息 )

2.2Android Studio通过注解提升代码检测

Android APT注解处理器 ( 配置注解依赖支持的注解类型Java 版本支持 )

Android Support Annotations

Android Support Annotations :安卓注解快速上手

Android APT注解处理器 ( Element 注解节点相关操作 )