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

[java] view plaincopy

  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,在这个类中来解析注解

[java] view plaincopy

  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、完成上面的步骤后就是如何去使用了,示例代码如下:

[java] view plaincopy

  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

时间: 2024-11-08 21:05:03

Android运行时注解的相关文章

android N 7.0之运行时注解|Mexicande

must be regedit "" in activity or this activity have annotation 最近在做一个新的项目的时候使用了一个别人的库,只是为了偷懒少敲一些代码,期限在测试一直没问题,但是在android 7.0上面测试,虽然提示must be regedit "" in activity or this activity have annotation 字面意思是,""库需要去注册或者这个activity使

Android 编译时注解-提升

Android 编译时注解-提升 背景 在前面的文章中,讲解了注解和编译时注解等一些列相关的内容,为了更加全面和真是的了解Android 编译时注解在实战项目中的使用,本文采取实现主流框架butterknife注入view去全面认识编译时注解. 注解专栏-博客 效果 先来张图压压惊,实现效果butterknife的view绑定 使用 仿照butterknife实现了@BindView注解,通过WzgJector.bind方法绑定当前MainActivity,整体和butterknife使用完全一

【Android运行时权限申请快速学习教程】

1. Android权限介绍 Android权限是Android应用在设备上运行所需要的权力,义务就是为用户带来此Android应用的功能. 问题来源:在Android6.0版本之前,根据App在AndroidManifest申请的权限,在安装此App的时候进行提示权限允许,不允许就不给装不给用,那么我们只能默默的忍受这个APP对我们隐私的侵蚀(Eg.通讯录的读取权限). 解决办法:后来Google发现,诶,这样会导致Android用户们想用这个APP的某些功能而不能用.因此,在Android6

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

对注解概念不了解的可以先看这个:Java注解基础概念总结 前面有提到注解按生命周期来划分可分为3类: 1.RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃: 2.RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期: 3.RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在: 这3个生

Qt for android运行时出错 Error: Target id &#39;android--1&#39; is not valid

[提问]windows7下Qt for android运行时出错 Error: Target id 'android--1' is not valid[复制链接] 上一主题下一主题   离线yijunziran 新手上路 加关注 发消息 只看楼主 倒序阅读楼主  发表于: 2015-04-11 程序是正确的,在Window环境下运行OKAndroid模拟器能跑起来. 在Android上运行时出现这种情况 22:19:48: 进程"D:\Qt\Qt5.4.1\Tools\mingw491_32\b

Android运行时配置更改

Android支持在运行时更改语言,位置.硬件.它是通过终止和重启activity来实现上诉功能的.可起到强制重新评估activity中使用的资源的分辨率,并为心得配置选择最合适的资源值. 要让activity可以监听运行时配置更改,需要向它的manifest节点中添加一个android:configChanges属性来说明希望对哪些配置更改进行处理 下面的列表描述了可以指定的运行时更改: mcc和mnc    检测到sim,并且与之关联的国家或者网络的代码发生了变化 locale    用户改

Android运行时动态权限获取

运行时动态权限简介 在targetSdkVersion小于23的应用中默认授予AndroidManifest中声明的所有权限,而无需手动授予.当targetSdkVersion在23或以上时,应用默认不会授予"Dangerous"级别的权限,Android默认只要授予该组一个权限即可获得该组的所有权限. 动态权限获取 下面将演示如何通过运行时权限获取来调用系统相机进行拍照和录制视频 首先以targetSdkVersion 23来编译调试应用,要调用相机拍照并存储需要在在AndroidM

Android运行时异常“Binary XML file line # : Error inflating class”

在原生Android下编译APK,编译没有问题,但是在运行的时候经常出现如标题所描述的异常,然后整个程序Crash掉...... 我遇到该问题常常都是因为修改了资源文件所引起,大致有以下几种方式来解决: 1. 引用类名问题:自定义了一个View,将他用于布局文件中,假设他的包名叫MyPackage,类名叫MyTestView,这个时候你在XML作为布局元素来布局的话,必须使用完整路径名,也就是包名加类名来引用,用MyPackage.MyTestView来进行引用. 2.构造函数问题:自定义一个V

Android(java)学习笔记152:Android运行时异常“Binary XML file line # : Error inflating class”

在原生Android下编译APK,编译没有问题,但是在运行的时候经常出现如标题所描述的异常,然后整个程序Crash掉......     我遇到该问题常常都是因为修改了资源文件所引起,大致有以下几种方式来解决:     1. 引用类名问题:自定义了一个View,将他用于布局文件中,假设他的包名叫MyPackage,类名叫MyTestView,这个时候你在XML作为布局元素来布局的话,必须使用完整路径名,也就是包名加类名来引用,用MyPackage.MyTestView来进行引用.     2.构