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 应用层知识纲要

    Java基础 * 面向对象 * Java集合框架 * 异常处理 * Java反射, Spring框架,通过反射实现 * 泛型, 静态变成语言 * 文件操作 Android基础 * Activity * ...

  2. 插件和过滤器装饰器开发中的感悟-python-django

    写这篇随笔是因为今天自己在写插件和过滤方法的过程中碰壁了,折腾了好久终于稍微发现些问题,在此记下,以作备忘. 在看了xadmin的插件机制后,笔者也想使用该思想来扩展kadmin中视图的方法. 例如, ...

  3. springtest+juint开发测试如下:

    项目结构目录如下: UserMapper.java 为接口文件.User 为实体类.UserMapper.xml 为对应mybatis的xml文件.test为对应的测试包 applicationtes ...

  4. Python 日期格式化 及 schwartzian排序

    __author__ = 'root' import datetime import time import copy # 12/Dec/2012:23:59:50 # 12/Sep/2012:23: ...

  5. hdu 5072 Coprime

    http://acm.hdu.edu.cn/showproblem.php?pid=5072 题意:给出 n 个互不相同的数,求满足以下条件的三元无序组的个数:要么两两互质要么两两不互质. 思路:根据 ...

  6. oracle审计

    Orcale审计机制研究 1.   设置审计 1.1.  查看审计状态 SQL>conn /as sysdba; 已连接 SQL>show parameters audit_trail; ...

  7. 【Linux】鸟哥的Linux私房菜基础学习篇整理(八)

    1. useradd [-u UID] [-g 初始用户组] [-G 次要用户组] [-mM]\   [-c 说明栏] [-d 主文件夹绝对路径] [-r] [-s shell] 用户账号名:新增用户 ...

  8. Hierarchy Viewer显示视图性能指标

    Hierarchy Viewer默认打开“Tree View”窗口无法显示显示Performance indicators: 但选中根视图再点击按钮“Obtain layout times for t ...

  9. 高效算法——G - 贪心

    G - 贪心 Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Desc ...

  10. trigger 触发器

    --trigger --在SC表上建了一个触发器,查看inserted和deleted表中内容. create trigger tri_1 on sc for insert,update,delete ...