书山有路勤为径,学海无涯苦做舟,苦海无涯,回头是岸

android注解,是JDK5.0引入的一种注解机制,主要是用于减少一些繁琐的工作,比如:findViewById...。现在有许多框架帮我们实现了相关的注解,方便且实用。但闲暇之余还是可以了解一下其中的原理,知其然也知其所以然。

下面我们就以简单的例子,来简单的学习一下android注解:

一、android 注解介绍:

1.1、注解解释:

JDK5.0内置了3个标准注解,4个元注解

@Override:检查方法是否是重写方法

@Desprecated:标记元素被废弃,使用该注解的元素,编译器会发出警告。

@SuppressWarnings:指示编译器忽略警告信息。

@Target:标记注解可用在什么地方(元注解)

@Retention:标记注解可用在什么地方(元注解)

@Documented:标记注解是否出现在 Javadoc 中(元注解)

@Inherited:标记标注的Annotation是否具有继承性(元注解)

在JDK7.0之后,额外增加了3个注解:

@SafeVarargs:忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告

@FunctionalInterface:标识一个匿名函数或函数式接口

@Repeatable:标识某注解可以在同一个声明上使用多次

注解有多个,详细的这儿不做过多介绍,自定义注解时,主要用到@Target和@Retention两个元注解,下面介绍一下这两个注解

1.2、@Target使用介绍

@Target:标记用于什么地方,接收参数为枚举ElementType中的元素,下面来看一下ElementType这个枚举:

public enum ElementType {

/** Class, interface (including annotation type), or enum declaration */

TYPE,

/** Field declaration (includes enum constants) */

FIELD,

/** Method declaration */

METHOD,

/** Formal parameter declaration */

PARAMETER,

/** Constructor declaration */

CONSTRUCTOR,

/** Local variable declaration */

LOCAL_VARIABLE,

/** Annotation type declaration */

ANNOTATION_TYPE,

/** Package declaration */

PACKAGE,

/**

* Type parameter declaration

*

* @since 1.8

*/

TYPE_PARAMETER,

/**

* Use of a type

*

* @since 1.8

*/

TYPE_USE

}

通过ElementType这个枚举,我们可以看出,@Target可以用于类、属性、方法、参数、构造方法等等地方,参数可以设置多个,例如:

@Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})

表示这个注解可用于属性、类、方法上面。

1.3、@Target使用介绍使用

@Retention:标记注解的策略,接收参数为枚举RetentionPolicy中的元素,我们来看一下RetentionPolicy:

public enum RetentionPolicy {

/**

* Annotations are to be discarded by the compiler.

*/

SOURCE,//源文件,编译时丢弃,class字节码文件中不包含

/**

* Annotations are to be recorded in the class file by the compiler

* but need not be retained by the VM at run time. This is the default

* behavior.

*/

CLASS,//默认策略,编译时保留,class字节码中存在,运行时丢弃,运行时无法获得

/**

* Annotations are to be recorded in the class file by the compiler and

* retained by the VM at run time, so they may be read reflectively.

*

* @see java.lang.reflect.AnnotatedElement

*/

RUNTIME//编译时保留,运行时保留,可通过反射获得

}

具体含义看代码注释,此处不再重复解释

二、自定义注解: 

2.1、定义注解

定义注解时,需要使用到@interface关键字,类似下面的定义;

@Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

public @interface Myannotation {

int viewId() default 0;

int layoutId() default 0;

int onclick() default 0;

}

 此处定义了一个可用于属性、类方法的注解,注解接收三个int类型的参数,但都有默认值,默认值为0 ,所以使用的时候可以只传入其中的一部分。

2.2、使用注解

2.1中定义的注解名称为Myannotation,使用注解的方式为在需要注解的属性、方法、类前面使用@Myannotation(参数)进行使用,例如以下代码:

@Myannotation(layoutId = R.layout.activity_main)//注解类

public class MainActivity extends AppCompatActivity {

private TXCloudVideoView mVideoViewAnchor;

@Myannotation(viewId = R.id.button_1)//注解属性

private Button button1;

@Myannotation(onclick = R.id.button_1)//注解方法

public void clickButton(View v){

Log.d("test", "被点击到了");

}

}

2.3、注解如何生效? 

注解不能凭空生效,需要我们去对他进行解释处理,例如以下代码:

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

AnnotionUtils.injectActivity(this);//处理注解

// TestAsyncTask task = new TestAsyncTask();

Integer[] years = new Integer[3];

button1.setText("click me,click me");

}

 利用 AnnotionUtils.injectActivity(this);这句代码对注解进行处理

接下来,我们从injectActivity(Activity activity)这个方法开始,看一下整个的处理流程:

public static void injectActivity(Activity activity){

if(activity.getClass().isAnnotationPresent(Myannotation.class)){

Myannotation myannotation = activity.getClass().getAnnotation(Myannotation.class);

activity.setContentView(myannotation.layoutId());

injectClick(activity);//处理点击方法

injectView(activity);//处理属性

}

}

 代码比较简单,主要的流程是通过isAnnotationPresent这个方法,判断指定类型的注释是否存在于此元素上,如果存在,则返回TRUE。,然后通过getAnnotation(Class annotationClass)方法,去获取到对应的注解对象,通过activity的setContentView绑定对应的布局文件layoutId.

接下来,我们再来看一下处理点击的方法,代码如下:

public static void injectClick(Activity activity){

Class activityClass = activity.getClass();

Method[] methods = activityClass.getMethods();//获取方法

for (int i = 0;i < methods.length;i++) {

if (methods[i].isAnnotationPresent(Myannotation.class)){

Myannotation myannotation = methods[i].getAnnotation(Myannotation.class);

Log.d("test", " | " + myannotation.onclick());

int finalI = i;

activity.findViewById(myannotation.onclick()).setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

try {

methods[finalI].invoke(activity,v);

} catch (IllegalAccessException e) {

e.printStackTrace();

} catch (InvocationTargetException e) {

e.printStackTrace();

}

}

});

}

}

}

 首先通过activityClass.getMethods()方法获取到class中所有的方法(注:此处涉及到反射的知识点,我们下期一起学习一下反射)

然后遍历所有的方法,通过isAnnotationPresent方法判断,方法中是否含有Myannotation注解,如果含有,则做以下两步操作:

1、对传入的控件id,设置OnClickListener监听器

2、在onclick方法中调用被注解的方法。这儿要使用到反射中的invoke方法,先在此处简单的介绍一下这个方法,方法生命如下:

第一个参数,是调用此方法的对象,比如我们此处是要调用activity中的clickButton方法,所以第一个参数传入activity对象,Object... args是一个可变参数,clickButton接收哪些参数,此处就传入哪些参数。

methods[finalI].invoke(activity,v);在示例中,这样就达到了在点击id为button_1的button时,调用了activity中的clickButton方法。

最后我们来看一下处理属性的方法,代码如下:

这儿流程比较简单,也涉及到了反射的相关知识,简单来说分为两步:

1、 调用activityClass.getDeclaredFields()获取所有属性

2、通过isAnnotationPresent判断属性是否含有Myannotation,如果有,调用属性的set方法对属性进行赋值。

这里流程比较简单,但有两处需要注意(注释中也说明了):

1、获取属性,注意getDeclaredFields和getFields的区别,getFields只能获取到public的属性

2、获取到的私有属性赋值前,一定不能忘记设置setAccessible为true,否则不能修改成功

完整代码这边如下:

Myannotation:

@Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

public @interface Myannotation {

int viewId() default 0;

int layoutId() default 0;

int onclick() default 0;

}

 MainActivity:

@Myannotation(layoutId = R.layout.activity_main)

public class MainActivity extends AppCompatActivity {

private TXCloudVideoView mVideoViewAnchor;

@Myannotation(viewId = R.id.button_1)

private Button button1;

@Myannotation(onclick = R.id.button_1)

public void clickButton(View v){

Log.d("test", "被点击到了");

}

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

AnnotionUtils.injectActivity(this);//处理注解

Integer[] years = new Integer[3];

button1.setText("click me,click me");

}

}

AnnotionUtils:

public class AnnotionUtils {

public static void injectActivity(Activity activity){

if(activity.getClass().isAnnotationPresent(Myannotation.class)){

Myannotation myannotation = activity.getClass().getAnnotation(Myannotation.class);

activity.setContentView(myannotation.layoutId());

injectClick(activity);//处理点击方法

injectView(activity);//处理属性

}

}

public static void injectView(Activity activity){

Class activityClass = activity.getClass();

Field[] fields = activityClass.getDeclaredFields();//获取属性,注意getDeclaredFields和getFields的区别,getFields只能获取到public的属性

for (int i = 0;i < fields.length;i++) {

if (fields[i].isAnnotationPresent(Myannotation.class)){

try {

Log.d("test", fields[i].getName() + " | " + fields[i].getAnnotation(Myannotation.class).viewId());

fields[i].setAccessible(true);//私有属性一定不能忘记设置access

fields[i].set(activity,activity.findViewById(fields[i].getAnnotation(Myannotation.class).viewId()));

} catch (IllegalAccessException e) {

e.printStackTrace();

}

}

}

}

public static void injectClick(Activity activity){

Class activityClass = activity.getClass();

Method[] methods = activityClass.getMethods();//获取方法

for (int i = 0;i < methods.length;i++) {

if (methods[i].isAnnotationPresent(Myannotation.class)){

Myannotation myannotation = methods[i].getAnnotation(Myannotation.class);

Log.d("test", " | " + myannotation.onclick());

int finalI = i;

activity.findViewById(myannotation.onclick()).setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

try {

methods[finalI].invoke(activity,v);

} catch (IllegalAccessException e) {

e.printStackTrace();

} catch (InvocationTargetException e) {

e.printStackTrace();

}

}

});

}

}

}

}

 xml布局文件:

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:context=".MainActivity">

android:id="@+id/button_1"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Hello World!"

app:layout_constraintBottom_toBottomOf="parent"

app:layout_constraintLeft_toLeftOf="parent"

app:layout_constraintRight_toRightOf="parent"

app:layout_constraintTop_toTopOf="parent" />

android:id="@+id/video_view_anchor"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:visibility="visible"

app:layout_constraintBottom_toBottomOf="parent"

app:layout_constraintEnd_toEndOf="parent"

app:layout_constraintStart_toStartOf="parent"

app:layout_constraintTop_toTopOf="parent" />

java反射:java 反射原理学习_weixin_43863193的博客-CSDN博客 

查看原文