Support Annotation Library是一个函数包,包含一系列有用的元注解。
注解目录:
实用主义至上,不记录历史和版本这些,现在直接了解一下这个函数包有什么用,知道作用了才有兴趣深入了解:
作用:
这些元注解可以帮助我们发现在开发中遇到的一些bug。凡是标注了元注解的地方在Proable bugs中,我们可以看到程序可能发生bug的地方,提前预知Bug在何处,提前避开bug。
使用步骤:
一、首先导入这个函数包,Android Studio是天然支持的。在project structure导入support-annotations这个包
二、对容易出错的类或者方法进行注解,下面写一个简单的Demo: 定义一个方法s(),对这个方法中的形参进行注解(蓝色字体是注解内容),注解为不可以为空,之后在主方法中调用这个方法,参数为空。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
s(null);
}
public String s(@NonNull String s){
return s;
}
}
三、现在我们特意写的错误写好了,接下来对整个项目的代码进行检查,看看Android Studio给我们返回的效果:
检查代码步骤:
查看可能存在的bug:
可以看到在控制台的Probable bugs中可以看到给我们自己注解后如果操作错误提示的错误
这是我们使用这个注解库的终极目标,方便我们使用自定义的类和方法,也便于我们找到未知的bug,对团队间合作作用也挺大的,而且我们使用系统的一些方法和类出错时对这些错误的理解也很有帮助
下面介绍一些注解的使用方式:
- @Nullable作用于函数参数或者返回值,标记参数或者返回值可以为空
- @NonNull作用函数参数或者返回值,标记参数或者返回值不可以为空
举个例子:
定义一个test()方法,这个方法的形参是String类型,给定注解是可以为null
在onCreate()方法中调用这个方法
我们知道资源在Android中通常是以整型值表示的,并保存在R.java文件中。这意味着一个需要传入Layout资源值的函数,如果传入String资源值不会再编译期报错,只有在运行时实行到相应的代码才能发现问题,则使用资源类型注解可以防止这种情况的出现。
资源类型的注解作用于函数参数、返回值及类的变量,每种资源类型对应一个注解。
- AnimatorRes:标记整型值是android.R.animator类型。
- AnimRes:标记整型值是android.R.anim类型
- AnyRes:标记整型值是任何一种资源类型,如果切确知道表示的是哪一种具体资源的话,建议显示指定。
- ArrayRes:标记整型值是android.R.array类型。
- AttrRes:标记整型值是android.R.attr类型。
- BoolRes:标记整型值是布尔类型。
- ColorRes:标记整型值是android.R.color类型。
- DrawableRes:标记整型值是android.R.drawable类型。
- FractionRes:标记整型值是fraction类型,这个比较少见,这种类型的资源常见于Animation Xml中,比如50%p,表示占parent的50%。
- IdRes:标记整型值是android.R.id类型。
- IntegerRes:标记整型值是android.R.integer类型。
- InterpolatorRes:标记整型值是android.R.interpolator类型。
- LayoutRes:标记整型值是android.R.layout类型。
- MenuRes:标记整型值是android.R.menu类型。
- PluralsRes:标记整型值是android.R.plurals类型,表示复数字符串类型。
- RawRes:标记整型值是android.R.raw类型。
- StringRes:标记整型值是android.R.string类型。
- StyleableRes:标记整型值是android.R.styleable类型。
- StyleResult:标记整型值是android.R.style类型。
- TransitionRes:标记整型值是transition类型。
- XmlRes:标记整型值是android..R.xml类型。
看个例子:
我先自定义一个方法test(),在这个方法中有一个形参int i,给这个形参的注解是@LayoutRes
接下来我在onCreat()方法中调用这个test()方法,传入的参数也是int类型的,但是不是R.Layout类型的
可以看到编译器会提示这个错误
在Android开发中,整型值不止经常用来代表资源引用值,而且经常用来代替枚举值(在Android中是不推荐使用枚举的),@InDef注解用来创建一个整型类型定义的新注解,我们可以使用这个新注解来标记自己编写的API。
举个例子:
先自定义注解
调用注解好的方法看看效果
可以看到如果传入这个方法的参数不是常量列表中的常量的话编译器是会报错的
Android应用开发过程中,经常会涉及多种线程的使用,界面相关操作必须在主线程里,而耗时操作例如文件下载等则可以放到后台线程中。线程相关注解有四种。
- @UiThread:标记运行在UI线程,一个UI线程是Activity运行所在的主窗口,对于一个应用而言,可能存在多个UI线程,每个线程对应不同的主窗口
- @MainThread:标记运行在主线程,一个应用只有一个主线程,主线程也是@UiThread线程。通常情况下,我们使用@MainThread来注解声明周期相关函数,使用@UiThread来注解视图相关函数,一般情况下,@UiThread和@MainThread是可以互换使用的
- @WorkerThread:标记运行在后台线程。
- @BinderThread: 标记运行在Binder线程。
一个很典型的例子就是AsyncTask的实现,下面我们自己来定义一个方法加上注解看看效果
package com.contentprovide.liuliu.test_4_21; import android.os.Bundle; import android.support.annotation.MainThread; import android.support.v7.app.AppCompatActivity; import android.widget.Toast; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(new Runnable() { @Override public void run() { a(); } }).start(); } @MainThread public void a() { Toast.makeText(getApplicationContext(), "123", Toast.LENGTH_LONG).show(); } }
在上面的代码中我自定义了一个方法a,给这个方法的元注解是@MainThread,然后在子线程new Thread()中调用这个方法,可以看到报以下错误
在资源类型注解中我们使用@ColorRes来标记参数类型需要传入颜色类型的资源id,而这里介绍的@ColorInt注解则是标记参数类型需要传入RGB或者ARGB颜色整型值。以下是TextView中的setTextColor()方法,这个方法的注解就是RGB颜色类型值,所以如果传入颜色类型的资源id就会报下面的错误
当函数参数的取值是在一定范围内时,可以使用值范围注解来防止调用者传入错误的参数,这种类型主要有三种注解
(1)@Size:对于类似数组、集合和字符串之类的参数,我们可以使用@Size注解来表示这些参数的大小。用法如下:
@Size(min=1)//可以表示集合不可以为空
@Size(max=23)//可以表示字符串最大字符个数是23
@Size(2)//可以表示数元素个数是2个
@Size(multiple=2)//可以表示数组的大小是2的倍数
(2)@IntRange:参数类型是int或者long,用法如下。
(3)@FloatRange:参数类型是float或者double,用法如下。
Android应用在使用某些系统功能时,需要在AndroidManifiest.xml中声明权限,否则在运行时会提示缺失相应的权限。为了在编译时及时发现权限的缺失,我们可以用@RequiresPermission注解
- 如果函数调用需要声明一个权限,语句如下。
@RequiresPermission(Manifest.permission.SET_WALLPAPER)
- 如果函数调用需要声明集合中最少一个权限,语句如下。
@RequiresPermission(anyOf = {
Manifest.permission.ACCESS_CHECKIN_PROPERTIES,
Manifest.permission.ACCESS_FINE_LOCATION
})
- 如果调用函数要同时声明多个权限,语句如下。
@RequiresPermission(allOf = {
Manifest.permission.ACCESS_CHECKIN_PROPERTIES,
Manifest.permission.ACCESS_FINE_LOCATION
})
- 对于Intent调用所需权限,可以在Intent的ACTION字符串定义处添加注解,语句如下。
@RequiresPermission(Manifest.permission.BLUETOOTH) public static final String ACTION_REQUEST_DISCOVERABLE= "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
- 对于ContentProvider相关的所需权限,可能同时需要读和写这两个操作,对应不同的权限声明,语句如下。
@RequiresPermission.Read(@RequiresPermission(READ_HISTORY_BOOKMARKS))
@RequiresPermission.Write(@RequiresPermission(WRITE_HISTORY_BOOKMARKS))
如果API允许调用者重写某个函数,但同时要求重写的函数需要调用被重写的函数,否者代码逻辑可能会错误,那么可以使用@CallSuper注解来提示开发者,语句如下是:
@CallSuper protected void onCreate(Bundle savedInstanceState);
但是其实我一直不知道这个注解和@Override的区别在哪里,有大神知道麻烦留言告诉我一下,谢谢啦。
如果我们编写的函数需要调用者对返回值做某些处理,那么可以使用@CheckResult注解来提示开发者。当然我们没有必要对每个非空返回值的函数都添加这个注解,该注解的主要目的是让调用者在使用API时不至于怀疑该函数是否会产生副作用。在Android源码中,Context类的checkPermission函数就使用了该注解。
单元测试中可能需要访问到一些不可见的类、函数或者变量,这是可以使用@VisibleForTesting注解来使其对测试可见。
@Keep注解用来标记在Proguard混淆过程中不需要混淆的类或者方法。
最后啰嗦:如果函数库中使用了Annotation Library,并使用Gradle生成ll压缩包,那么在编译时Android Gradle插件会抽取出这些注解信息并打包在ll文件中,以便函数库的调用者正常使用我们的注解信息。ll文件中的annotations.zip文件就是抽取出来的注解信息