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的注解是如何工作的,如果你不了解可以先看一下相关的资料,这个比较简答。首先定义我们的注解类:

  1. /**
  2. * view inect by id
  3. *
  4. * @author Lucky
  5. *
  6. */
  7. @Target(ElementType.FIELD)//表示用在字段上
  8. @Retention(RetentionPolicy.RUNTIME)//表示在生命周期是运行时
  9. public @interface ViewInject {
  10. int value() default 0;
  11. }

2、我们需要定义个BaseActivity,在这个类中来解析注解

  1. /**
  2. *
  3. * @author Lucky
  4. *
  5. */
  6. public abstract class BaseActivity extends FragmentActivity {
  7. /**
  8. * get content view layout id
  9. *
  10. * @return
  11. */
  12. public abstract int getLayoutId();
  13. @Override
  14. protected void onCreate(Bundle savedInstanceState) {
  15. super.onCreate(savedInstanceState);
  16. setContentView(getLayoutId());
  17. autoInjectAllField();
  18. }
  19. /**
  20. * 解析注解
  21. */
  22. public void autoInjectAllField() {
  23. try {
  24. Class<?> clazz = this.getClass();
  25. Field[] fields = clazz.getDeclaredFields();//获得Activity中声明的字段
  26. for (Field field : fields) {
  27. // 查看这个字段是否有我们自定义的注解类标志的
  28. if (field.isAnnotationPresent(ViewInject.class)) {
  29. ViewInject inject = field.getAnnotation(ViewInject.class);
  30. int id = inject.value();
  31. if (id > 0) {
  32. field.setAccessible(true);
  33. field.set(this, this.findViewById(id));//给我们要找的字段设置值
  34. }
  35. }
  36. }
  37. } catch (IllegalAccessException e) {
  38. e.printStackTrace();
  39. } catch (IllegalArgumentException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. }


3、完成上面的步骤后就是如何去使用了,示例代码如下:

  1. public class TestActivity extends BaseActivity {
  2. @ViewInject(R.id.claim_statement)
  3. private WebView mWebView;
  4. @Override
  5. public int getLayoutId() {
  6. // TODO Auto-generated method stub
  7. return R.layout.activity_claim;
  8. }
  9. }
注解反射只能提高写代码的效率,但是程序的执行效率确实相反的方向,不过影响不大。

三、参考资料

1、http://www.2cto.com/kf/201405/302998.html

Android运行时注解的更多相关文章

  1. Java 进阶巩固:什么是注解以及运行时注解的使用

    这篇文章 2016年12月13日星期二 就写完了,当时想着等写完另外一篇关于自定义注解的一起发.结果没想到这一等就是半年多 - -. 有时候的确是这样啊,总想着等条件更好了再干,等准备完全了再开始,结 ...

  2. 自定义注解之运行时注解(RetentionPolicy.RUNTIME)

    对注解概念不了解的可以先看这个:Java注解基础概念总结 前面有提到注解按生命周期来划分可分为3类: 1.RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成clas ...

  3. Android 运行时权限处理(from jianshu)

    https://www.jianshu.com/p/e1ab1a179fbb 翻译的国外一篇文章. android M 的名字官方刚发布不久,最终正式版即将来临! android在不断发展,最近的更新 ...

  4. Qt for android运行时出错 Error: Target id 'android--1' is not valid

    [提问]windows7下Qt for android运行时出错 Error: Target id 'android--1' is not valid[复制链接] 上一主题下一主题   离线yijun ...

  5. Dalvik模式下基于Android运行时类加载的函数dexFindClass脱壳

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/78003184 前段时间在看雪论坛发现了<发现一个安卓万能脱壳方法>这篇 ...

  6. Android中的自定义注解(反射实现-运行时注解)

    预备知识: Java注解基础 Java反射原理 Java动态代理 一.布局文件的注解 我们在Android开发的时候,总是会写到setContentView方法,为了避免每次都写重复的代码,我们需要使 ...

  7. Android 编译时注解解析框架

    2.注解 说道注解,竟然还有各种分类,得,这记不住,我们从注解的作用来反推其分类,帮助大家记忆,然后举例强化大家的记忆,话说注解的作用: 1.标记一些信息,这么说可能太抽象,那么我说,你见过@Over ...

  8. 利用APT实现Android编译时注解

    摘要: 一.APT概述 我们在前面的java注解详解一文中已经讲过,可以在运行时利用反射机制运行处理注解.其实,我们还可以在编译时处理注解,这就是不得不说官方为我们提供的注解处理工具APT (Anno ...

  9. Android运行时注入浅析与使用

    背景 最近接触新项目,项目中引入了Android Annotation(AA)依赖注入开源框架,代码中大片的注解代码,对于没用过注解框架(或者说没有如此大面积的使用)的我来说确实看得很费力,于是花时间 ...

随机推荐

  1. Android JSON 解析库的使用 - Gson 和 fast-json

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.它基于ECMAScript的一个子集. JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族 ...

  2. 【Python开发实战】Python环境的配置

    1. 安装Pythonsudo aptitude -y install python-dev 安装Distribute:支撑模块构建与导入的包sudo chmod -R 0775 /usr/local ...

  3. Django socketio 安装

    如果你还没有安装过 gevent,首先需要安装 libevent, 编译安装 libevent 需要安装 Pyhton 开发库. 在Debain上可以运行如下指令: $ sudo apt-get in ...

  4. js的引用顺序

    注意:Bootstrap中的JS插件依赖于JQuery,因此JQuery要在Bootstrap之前引用!!! 把JS文件引用放入body的最下面,是为了使js在网页全部加载完后才起作用,比如你的js里 ...

  5. 转:简单介绍 P3P 技术

    原文来自于:http://blog.csdn.net/ghj1976/article/details/4889219 以 Internet Explorer 为例,默认情况下,IE的隐私策略如下图所设 ...

  6. pycharm去掉拼写检查

    http://zhidao.baidu.com/question/523436629.html

  7. Oracle中数字格式的文本化处理

    Select TO_CHAR(123.0233,'FM999,999,90.09') FROM DUAL 1.小数点后面的0指示至少保留1位小数,9表示最多保留两位小数 2.小数点前面的0指示至少保留 ...

  8. 这个知识点不错,,学习一下先。。。无状态服务(stateless service)(转)

    这样的应用,显得高级一些哟~~:) +================== http://kyfxbl.iteye.com/blog/1831869 ========================= ...

  9. java中String s="abc"及String s=new String("abc")详解

    1.   栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方.与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆. 2.   栈的优势是,存取速度比堆要快,仅次于直 ...

  10. BagTest

    package cn.aust.zyw.demo; import java.util.Iterator; /** * Created by zyw on 2016/2/17. */ public cl ...