转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/119874435 本文出自【赵彦军的博客】
Java注解
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。 Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。
Java内置了多种标准注解,其定义在java.lang中。
-
Override 表示当前的方法定义将覆盖父类中的方法  -
Deprecated 被此注解标记的元素表示被废弃  -
SuppressWarnings 关闭不当的编译器警告信息  在上面我们看到了 @Target 、@Retention , 这些也是注解,我们暂且可以称之为注解的注解。
元注解说明
@Retention
表示需要在什么级别保留该注解信息
- RetentionPolicy.SOURCE:只在源代码中保留 一般都是用来增加代码的理解性或者帮助代码检查之类的,比如我们的Override
- RetentionPolicy.CLASS : 默认的选择,能把注解保留到编译后的字节码class文件中,仅仅到字节码文件中,运行时是无法得到的
- RetentionPolicy.RUNTIME :注解不仅能保留到class字节码文件中,还能在运行通过反射获取到,这也是我们最常用的
@Target
表示该注解可以用在什么地方
- ElementType.FIELD : 能修饰成员变量
- ElementType.METHOD:能修饰方法
- ElementType.CONSTRUCTOR : 能修饰构造器
- ElementType.PACKAGE : 能修饰包
- ElementType.PARAMETER : 能修饰方法参数
- ElementType.TYPE : 能修饰类、接口或枚举类型
- ElementType.ANNOTATION_TYPE : 能修饰注解
- ElementType.LOCAL_VARIABLE:能修饰局部变量
- ElementType.MODULE :
通过反射获取注解信息
Annotation是被动的元数据,永远不会有主动行为,所以我们需要通过使用反射,才能让我们的注解产生意义。
首先我们定义一个端口注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Port {
String value() default "8080";
}
RetentionPolicy.RUNTIME 在运行时保留注解,作用于是字段。
反射获取字段
object BindPort {
fun bind(activity: Activity) {
val fields = activity.javaClass.declaredFields
fields.forEach { field ->
val ans = field.annotations
ans.forEach {
if (it is Port) {
var port = it.value
field.isAccessible = true
field.set(activity, port)
}
}
}
}
}
上面代码的逻辑很简单:
首先遍历循环所有的属性,如果当前属性被指定的注解所修饰,那么就将当前属性的值修改为注解中成员变量的值。
这里setAccessible(true)的使用时因为,我们在声明port变量时,其类型为private,为了确保可以访问这个变量,防止程序出现异常。
注解的使用:
class MainActivity : AppCompatActivity() {
@Port
var port: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d("yy--", "反射前:$port")
BindPort.bind(this)
Log.d("yy--", "反射后:$port")
}
}
运行结果:
com.example.myapplication D/yy--: 反射前:null
com.example.myapplication D/yy--: 反射后:8080
当然,我们也可以在注解时,自定义我们的属性值,比如:
class MainActivity : AppCompatActivity() {
@Port("8090")
var port: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d("yy--", "反射前:$port")
BindPort.bind(this)
Log.d("yy--", "反射后:$port")
}
}
运行结果:
com.example.myapplication D/yy--: 反射前:null
com.example.myapplication D/yy--: 反射后:8090
方法使用注解
我们来模拟一个http请求, 定义一个 Request 注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Request {
Method value();
enum Method {
GET, POST
}
}
注解的方式简单,@Retention(RetentionPolicy.RUNTIME) 在运行时保留,@Target(ElementType.METHOD) 作用域在方法上。
下面我们编写,反射的方法,定义 HttpBind
object HttpBind {
fun bind(activity: Activity) {
val methods = activity.javaClass.methods
methods.forEach { method ->
val ans = method.annotations
ans.forEach {
if (it is Request) {
var requestMethod = it.value
if (requestMethod == Request.Method.GET) {
method.invoke(activity, requestMethod.name)
} else if (requestMethod == Request.Method.POST) {
method.invoke(activity, requestMethod.name)
}
}
}
}
}
}
注解使用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
HttpBind.bind(this)
}
@Request(Request.Method.GET)
fun http(method: String) {
Log.d("yy--", "网络请求:$method")
}
}
我们运行一下,看看效果
D/yy--: 网络请求:GET
方法的参数使用注解
先定义一个参数注解 Path
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Path {
String value() default "";
}
这个注解也很简单,在运行时保留,作用域在参数上
下面我们使用反射来获取参数注解的值
object HttpBind {
fun bind(activity: Activity) {
val methods = activity.javaClass.methods
methods.forEach { method ->
val ans = method.parameterAnnotations
ans.forEach { annotationArray ->
annotationArray.forEach { parameterAnnotation ->
if (parameterAnnotation is Path) {
var parameter = parameterAnnotation.value
method.invoke(activity, parameter)
}
}
}
}
}
}
使用如下:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
HttpBind.bind(this)
}
fun http(@Path("zhaoyanjun") user: String) {
Log.d("yy--", "参数注解:$user")
}
}
运行结果如下:
D/yy--: 参数注解:zhaoyanjun
Android 自带的注解
Android 系统已经帮我内置了很多有用的注解,在我们的开发过程中可以很方便的使用。
implementation 'androidx.annotation:annotation:1.2.0'
示例如下  我们举几个常见的例子:
资源限制类
- @AnimatorRes :animator资源类型
- @AnimRes:anim资源类型
- @AnyRes:任意资源类型
- @ArrayRes:array资源类型
- @AttrRes:attr资源类型
- @BoolRes:boolean资源类型
- @ColorRes:color资源类型
- @DimenRes:dimen资源类型。
- @DrawableRes:drawable资源类型。
- @FractionRes:fraction资源类型
- @IdRes:id资源类型
- @IntegerRes:integer资源类型
- @InterpolatorRes:interpolator资源类型
- @LayoutRes:layout资源类型
- @MenuRes:menu资源类型
- @PluralsRes:plurals资源类型
- @RawRes:raw资源类型
- @StringRes:string资源类型
- @StyleableRes:styleable资源类型
- @StyleRes:style资源类型
- @TransitionRes:transition资源类型
- @XmlRes:xml资源类型
线程限制类
Thread annotations 线程执行限制类:用于限制方法或者类必须在指定的线程执行。如果方法代码运行线程和标注的线程不一致,则会导致警告。
- @AnyThread
- @BinderThread
- @MainThread
- @UiThread
- @WorkerThread
数值限制类
Value Constraint Annotations 类型范围限制类:用于限制标注值的值范围
LayoutRes
这个是 layout资源类型,我们看一下 Activity 的 setContentView 源码:
@Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
本质上,layoutResID 是一个 int 类型,如果不做限定的话,可以传入任意整形,但是有 @LayoutRes 注解的限制,值只能传入 R.layou.xx , 如果传入其他的类型就会报错。举例如下:  需要注意的是,报错只是编译器的检查出错,提醒开发者改正错误用法,提前规避风险,并不影响编译运行
@MainThread
限定方法执行的线程,如果方法代码运行线程和标注的线程不一致,不会报错,更多是起一个提醒作用
@MainThread
fun run() {
}
|