Android的注解有编译时注解和运行时注解,本文就介绍下运行时注解。
其实非常简单,直接上代码:本文主要是替代传统的findViewById()的功能,就是在我们Activity中不需要再使用findViewById()去给View赋值了,通过注解在运行阶段自动赋值。以及setOnClickListener()也是一样的原理。使用注解和反射技术。
1. 定义自己的annotation注解。
定义findViewbyId这个功能的注解
package com.xxx.xxx.xxx; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView {
int value() default (int) -1;
}
@Target类型如下几种:
public enum ElementType {
/**
* Class, interface or enum declaration.
*/
TYPE,
/**
* Field declaration.
*/
FIELD,
/**
* Method declaration.
*/
METHOD,
/**
* Parameter declaration.
*/
PARAMETER,
/**
* Constructor declaration.
*/
CONSTRUCTOR,
/**
* Local variable declaration.
*/
LOCAL_VARIABLE,
/**
* Annotation type declaration.
*/
ANNOTATION_TYPE,
/**
* Package declaration.
*/
PACKAGE
}
@Retation类型为:
public enum RetentionPolicy {
/**
* Annotation is only available in the source code.
*/
SOURCE,
/**
* Annotation is available in the source code and in the class file, but not
* at runtime. This is the default policy.
*/
CLASS,
/**
* Annotation is available in the source code, the class file and is
* available at runtime.
*/
RUNTIME
}
定义setOnclickListener的注解
package com.xxx.xxx.xxx;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectClick {
int[] value();
}
2. 定义自己的注解处理类:
package com.xxx.xxx.xxx; import android.app.Activity;
import android.view.View; import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method; public class RunTimeInjector {
public static void injectView(Object obj, Object root) {
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
Annotation[] annotations = field.getAnnotations();
if (annotations != null) {
for (Annotation annotation : annotations) {
if (annotation instanceof InjectView) {
InjectView injectView = (InjectView) annotation;
int value = injectView.value();
if (value != -1) {
try {
View view = getViewByRoot(root, value);
field.set(obj, view);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
break;
}
}
}
}
} public static void injectClick(Object obj, Object root) {
Method[] methods = obj.getClass().getDeclaredMethods();
for (Method method : methods) {
Annotation[] annotations = method.getAnnotations();
if (annotations != null) {
for (Annotation annotation : annotations) {
if (annotation instanceof InjectClick) {
InjectClick inject = (InjectClick) annotation;
int[] value = inject.value();
if (value != null && value.length > 0) {
View.OnClickListener listener = (View.OnClickListener) obj;
try {
for (int res : value) {
View view = getViewByRoot(root, res);
if (view == null) {
throw new NullPointerException();
}
view.setOnClickListener(listener);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
} else if (annotation instanceof InjectLongClick) {
InjectLongClick inject = (InjectLongClick) annotation;
int[] value = inject.value();
if (value != null && value.length > 0) {
View.OnLongClickListener listener = (View.OnLongClickListener) obj;
try {
for (int res : value) {
View view = getViewByRoot(root, res);
if (view == null) {
throw new NullPointerException();
}
view.setOnLongClickListener(listener);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
}
}
}
}
} public static View getViewByRoot(Object root, int res) {
View view = null;
if (root instanceof Activity) {
view = ((Activity)root).findViewById(res);
}
return view;
} }
3. Activity中使用注解:
@InjectView(R.id.action_back)
private ImageView actionBack;
@InjectView(R.id.site_top_bg)
private ImageView mSiteTopBg;
@InjectView(R.id.site_name)
private TextView mSiteName;
@InjectView(R.id.site_producter)
private TextView mProducter;
@InjectView(R.id.site_logo)
private ImageRoundView mSiteLogo;
@InjectView(R.id.site_description)
private TextView mSiteDescription;
@InjectView(R.id.viewPager)
private ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_site);
RunTimeInjector.injectView(this, this);
RunTimeInjector.injectClick(this, this);
}
@Override
@InjectClick({R.id.action_back})
public void onClick(View v) {
int id = v.getId();
switch (id){
case R.id.action_back:
SiteDetailActivity.this.finish();
break;
}
}
===============================
注解
如果没有用来读取注解的方法和工作,那么注解也就不会比注释更有用处了。使用注解的过程中,很重要的一部分就是创建于使用注解处理器。Java SE5扩展了反射机制的API,以帮助程序员快速的构造自定义注解处理器。
注解处理器类库(java.lang.reflect.AnnotatedElement):
Java使用Annotation接口来代表程序元素前面的注解,该接口是所有Annotation类型的父接口。除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,该接口代表程序中可以接受注解的程序元素,该接口主要有如下几个实现类:
Class:类定义
Constructor:构造器定义
Field:累的成员变量定义
Method:类的方法定义
Package:类的包定义
java.lang.reflect 包下主要包含一些实现反射功能的工具类,实际上,java.lang.reflect 包所有提供的反射API扩充了读取运行时Annotation信息的能力。当一个Annotation类型被定义为运行时的Annotation后,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。
AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的如下四个个方法来访问Annotation信息:
方法1:<T extends Annotation> T getAnnotation(Class<T> annotationClass): 返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
方法2:Annotation[] getAnnotations():返回该程序元素上存在的所有注解。
方法3:boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
方法4:Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。
一个简单的注解处理器:
/***********注解声明***************/ /**
* 水果名称注解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
} /**
* 水果颜色注解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
/**
* 颜色枚举
* @author peida
*
*/
public enum Color{ BULE,RED,GREEN}; /**
* 颜色属性
* @return
*/
Color fruitColor() default Color.GREEN; } /**
* 水果供应者注解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
/**
* 供应商编号
* @return
*/
public int id() default -1; /**
* 供应商名称
* @return
*/
public String name() default ""; /**
* 供应商地址
* @return
*/
public String address() default "";
} /***********注解使用***************/ public class Apple { @FruitName("Apple")
private String appleName; @FruitColor(fruitColor=Color.RED)
private String appleColor; @FruitProvider(id=1,name="陕西红富士集团",address="陕西省西安市延安路89号红富士大厦")
private String appleProvider; public void setAppleColor(String appleColor) {
this.appleColor = appleColor;
}
public String getAppleColor() {
return appleColor;
} public void setAppleName(String appleName) {
this.appleName = appleName;
}
public String getAppleName() {
return appleName;
} public void setAppleProvider(String appleProvider) {
this.appleProvider = appleProvider;
}
public String getAppleProvider() {
return appleProvider;
} public void displayName(){
System.out.println("水果的名字是:苹果");
}
} /***********注解处理器***************/ public class FruitInfoUtil {
public static void getFruitInfo(Class<?> clazz){ String strFruitName=" 水果名称:";
String strFruitColor=" 水果颜色:";
String strFruitProvicer="供应商信息:"; Field[] fields = clazz.getDeclaredFields(); for(Field field :fields){
if(field.isAnnotationPresent(FruitName.class)){
FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
strFruitName=strFruitName+fruitName.value();
System.out.println(strFruitName);
}
else if(field.isAnnotationPresent(FruitColor.class)){
FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);
strFruitColor=strFruitColor+fruitColor.fruitColor().toString();
System.out.println(strFruitColor);
}
else if(field.isAnnotationPresent(FruitProvider.class)){
FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);
strFruitProvicer=" 供应商编号:"+fruitProvider.id()+" 供应商名称:"+fruitProvider.name()+" 供应商地址:"+fruitProvider.address();
System.out.println(strFruitProvicer);
}
}
}
} /***********输出结果***************/
public class FruitRun { /**
* @param args
*/
public static void main(String[] args) { FruitInfoUtil.getFruitInfo(Apple.class); } } ====================================
水果名称:Apple
水果颜色:RED
供应商编号:1 供应商名称:陕西红富士集团 供应商地址:陕西省西安市延安路89号红富士大厦
Java注解的基础知识点(见下面导图)基本都过了一遍,下一篇我们通过设计一个基于注解的简单的ORM框架,来综合应用和进一步加深对注解的各个知识点的理解和运用。
一、引言
Android中通过findViewById在布局文件中找到需要的View,加入一个Activity里面有许多的View需要初始化,那将是一件很繁琐的事情。当然Google一下你会发现有很多Android Annotations框架。比如比较有名的“Android Annotations”,这样的框架很复杂,用起来也比较麻烦,还有一些BUG,第一次使用也花费了不少时间研究。也许你在项目中只希望用到 Inject View这个功能,又或者你想知道这个实现的原理是怎样的。本文主要是解决这两个问题,实现一个最简单的ViewInject.
二、原理
原理是在Activity加载好后通过找到Activity中使用注解的字段,再通过Java反射的方式,动态的给这个字段设置值。
1、首先你需要了解一下Java的注解是如何工作的,如果你不了解可以先看一下相关的资料,这个比较简答。首先定义我们的注解类:
- /**
- * view inect by id
- *
- * @author Lucky
- *
- */
- @Target(ElementType.FIELD)//表示用在字段上
- @Retention(RetentionPolicy.RUNTIME)//表示在生命周期是运行时
- public @interface ViewInject {
- int value() default 0;
- }
2、我们需要定义个BaseActivity,在这个类中来解析注解
- /**
- *
- * @author Lucky
- *
- */
- public abstract class BaseActivity extends FragmentActivity {
- /**
- * get content view layout id
- *
- * @return
- */
- public abstract int getLayoutId();
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(getLayoutId());
- autoInjectAllField();
- }
- /**
- * 解析注解
- */
- public void autoInjectAllField() {
- try {
- Class<?> clazz = this.getClass();
- Field[] fields = clazz.getDeclaredFields();//获得Activity中声明的字段
- for (Field field : fields) {
- // 查看这个字段是否有我们自定义的注解类标志的
- if (field.isAnnotationPresent(ViewInject.class)) {
- ViewInject inject = field.getAnnotation(ViewInject.class);
- int id = inject.value();
- if (id > 0) {
- field.setAccessible(true);
- field.set(this, this.findViewById(id));//给我们要找的字段设置值
- }
- }
- }
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- }
- }
- }
3、完成上面的步骤后就是如何去使用了,示例代码如下:
- public class TestActivity extends BaseActivity {
- @ViewInject(R.id.claim_statement)
- private WebView mWebView;
- @Override
- public int getLayoutId() {
- // TODO Auto-generated method stub
- return R.layout.activity_claim;
- }
- }
注解反射只能提高写代码的效率,但是程序的执行效率确实相反的方向,不过影响不大。