Android运行时注解
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;
- }
- }
注解反射只能提高写代码的效率,但是程序的执行效率确实相反的方向,不过影响不大。
三、参考资料
1、http://www.2cto.com/kf/201405/302998.html
Android运行时注解的更多相关文章
- Java 进阶巩固:什么是注解以及运行时注解的使用
这篇文章 2016年12月13日星期二 就写完了,当时想着等写完另外一篇关于自定义注解的一起发.结果没想到这一等就是半年多 - -. 有时候的确是这样啊,总想着等条件更好了再干,等准备完全了再开始,结 ...
- 自定义注解之运行时注解(RetentionPolicy.RUNTIME)
对注解概念不了解的可以先看这个:Java注解基础概念总结 前面有提到注解按生命周期来划分可分为3类: 1.RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成clas ...
- Android 运行时权限处理(from jianshu)
https://www.jianshu.com/p/e1ab1a179fbb 翻译的国外一篇文章. android M 的名字官方刚发布不久,最终正式版即将来临! android在不断发展,最近的更新 ...
- Qt for android运行时出错 Error: Target id 'android--1' is not valid
[提问]windows7下Qt for android运行时出错 Error: Target id 'android--1' is not valid[复制链接] 上一主题下一主题 离线yijun ...
- Dalvik模式下基于Android运行时类加载的函数dexFindClass脱壳
本文博客地址:http://blog.csdn.net/qq1084283172/article/details/78003184 前段时间在看雪论坛发现了<发现一个安卓万能脱壳方法>这篇 ...
- Android中的自定义注解(反射实现-运行时注解)
预备知识: Java注解基础 Java反射原理 Java动态代理 一.布局文件的注解 我们在Android开发的时候,总是会写到setContentView方法,为了避免每次都写重复的代码,我们需要使 ...
- Android 编译时注解解析框架
2.注解 说道注解,竟然还有各种分类,得,这记不住,我们从注解的作用来反推其分类,帮助大家记忆,然后举例强化大家的记忆,话说注解的作用: 1.标记一些信息,这么说可能太抽象,那么我说,你见过@Over ...
- 利用APT实现Android编译时注解
摘要: 一.APT概述 我们在前面的java注解详解一文中已经讲过,可以在运行时利用反射机制运行处理注解.其实,我们还可以在编译时处理注解,这就是不得不说官方为我们提供的注解处理工具APT (Anno ...
- Android运行时注入浅析与使用
背景 最近接触新项目,项目中引入了Android Annotation(AA)依赖注入开源框架,代码中大片的注解代码,对于没用过注解框架(或者说没有如此大面积的使用)的我来说确实看得很费力,于是花时间 ...
随机推荐
- PHP面向对象(OOP):抽象方法和抽象类(abstract)
在OOP语言中,一个类可以有一个或多个子类,而每个类都有至少一个公有方法做为外部代码访问其的接口.而抽象方法就是为了方便继承而引入的,我们先来看一下抽象类和抽象方法的定义再说明它的用途. 什么是抽象方 ...
- 手机端的META你有多了解?
我们先来简单了解下meta标签:meta指元素可提供有关页面的元信息(meta-information),比如针对搜索引擎和更新频度的描述和关键词. 标签位于文档的头部,不包含任何内容. 标签的属性定 ...
- 隐藏和显示 ng-show ng-hide
<div ng-controller='DeathraymenueController'> <button ng-click="toggleMenue()" ...
- jQuery在on绑定事件时,使用Function.prototype.bind上下文,只能用off(event)解绑函数,否则可能导致事件叠加
因为一个bind函数,未解绑成功导致事件叠加,搞了一下午. keyup事件绑定: this.$document.on('keyup', this.keyUp.bind(this)); 原解绑函数: t ...
- TortoiseSVN 的分支合并操作
今天对svn的分支合并有了兴趣,所以对新建了一个项目练练手. 在网上找了一篇文章做指导: http://www.open-open.com/lib/view/open1346982569725.htm ...
- BZOJ 1070 修车
Description 同一时刻有\(N\)位车主带着他们的爱车来到了汽车维修中心.维修中心共有\(M\)位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的.现在需要安排这\(M\)位技术 ...
- 使用单调队列优化的 O(nm) 多重背包算法
我搜索了一下,找到了一篇很好的博客,讲的挺详细:链接. 解析 多重背包的最原始的状态转移方程: 令 c[i] = min(num[i], j / v[i]) f[i][j] = max(f[i-1][ ...
- [Forward]Visual Guide: Setting up My Sites in SharePoint 2013
from http://blog.sharedove.com/adisjugo/index.php/2012/07/25/visual-guide-setting-up-my-sites-in-sh ...
- Java Integer类分析
public static final int MIN_VALUE = 0x80000000; -2^31 public static final int MAX_VALUE = 0x7ff ...
- COJ 2135 Day10-例1
Day10-例1 难度级别:B: 运行时间限制:1000ms: 运行空间限制:256000KB: 代码长度限制:2000000B 试题描述 给定N个物品,价格分别为A1, A2…AN.设计一套面值互不 ...