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在CODE上查看代码片派生到我的代码片
 
 
  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在CODE上查看代码片派生到我的代码片
 
 
  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.   
  14.   
  15.     @Override  
  16.     protected void onCreate(Bundle savedInstanceState) {  
  17.         super.onCreate(savedInstanceState);  
  18.         setContentView(getLayoutId());  
  19.         autoInjectAllField();  
  20.     }  
  21.     /** 
  22.      * 解析注解 
  23.      */  
  24.     public void autoInjectAllField() {  
  25.         try {  
  26.             Class<?> clazz = this.getClass();  
  27.             Field[] fields = clazz.getDeclaredFields();//获得Activity中声明的字段  
  28.             for (Field field : fields) {  
  29.                 // 查看这个字段是否有我们自定义的注解类标志的  
  30.                 if (field.isAnnotationPresent(ViewInject.class)) {  
  31.                     ViewInject inject = field.getAnnotation(ViewInject.class);  
  32.                     int id = inject.value();  
  33.                     if (id > 0) {  
  34.                         field.setAccessible(true);  
  35.                         field.set(this, this.findViewById(id));//给我们要找的字段设置值  
  36.                     }  
  37.                 }  
  38.             }  
  39.         } catch (IllegalAccessException e) {  
  40.             e.printStackTrace();  
  41.         } catch (IllegalArgumentException e) {  
  42.             e.printStackTrace();  
  43.         }  
  44.     }  
  45. }  


 


 

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

 

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
 
  1. public class TestActivity extends BaseActivity {  
  2.   
  3.     @ViewInject(R.id.claim_statement)  
  4.     private WebView mWebView;  
  5.           
  6.   
  7.     @Override  
  8.     public int getLayoutId() {  
  9.         // TODO Auto-generated method stub  
  10.         return R.layout.activity_claim;  
  11.     }  
  12.   
  13. }  
注解反射只能提高写代码的效率,但是程序的执行效率确实相反的方向,不过影响不大。

 

 

三、参考资料

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

posted @ 2016-08-15 16:48  linghu_java  阅读(2227)  评论(0编辑  收藏  举报