Android开发 打造自己的Annotation框架

来源:互联网 发布:广州恒大淘宝股票 编辑:程序博客网 时间:2024/05/20 06:24

最近在回顾注解和反射方面的知识。

之前在项目开发过程中,也曾经体验过ButterKnife的注解,想结合反射和注解自己写一个框架。结合着大牛的博客,和自己的理解。实现了Activity加载layout和view初始化的注解。

一.原理

使用注解实现这样的功能,原理是什么呢?
反射+自定义annotation
首先自定义一个annotation
1.用于初始化View的自定义Annotation
package csu.lzw.reviewandroid.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * Created by Allen_Binan on 2016/4/2. */@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface LBindView {    int id() default -1;}

@Target用来表示这个注解修饰的对象是什么,FIELD表示是成员变量,例如Actvity当中的View控件。

@Retentiong表示这个注解是在什么级别保存信息。RUNTIME表示是运行时。


@interface是用于自定义注解的,它里面定义的方法的声明不能有参数,也不能抛出异常,并且方法的返回值被限制为简单类型、String、Class、emnus、@interface,和这些类型的数组。

    注解@Target也是用来修饰注解的元注解,它有一个属性ElementType也是枚举类型,值为:ANNOTATION_TYPE,CONSTRUCTOR ,FIELD,LOCAL_VARIABLE,METHOD,PACKAGE,PARAMETER和TYPE,如@Target(ElementType.METHOD) 修饰的注解表示该注解只能用来修饰在方法上。

     @RetentionRetention注解表示需要在什么级别保存该注释信息,用于描述注解的生命周期,它有一个RetentionPolicy类型的value,是一个枚举类型,它有以下的几个值:

     1.用@Retention(RetentionPolicy.SOURCE)修饰的注解,指定注解只保留在源文件当中,编译成类文件后就把注解去掉;
     2.用@Retention(RetentionPolicy.CLASS)修饰的注解,指定注解只保留在源文件和编译后的class 文件中,当jvm加载类时就把注解去掉;
     3.用@Retention(RetentionPolicy.RUNTIME )修饰的注解,指定注解可以保留在jvm中,这样就可以使用反射获取信息了。

     默认是RUNTIME,这样我们才能在运行的时候通过反射获取并做对应的逻辑处理。


同理可得,用于修饰Activity类,而加载layout布局的注解。


package csu.lzw.reviewandroid.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * Created by Allen_Binan on 2016/4/2. */@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface LBindContentView {    int layoutId() default -1;}


最终,在Activity中实现的效果如下

package csu.lzw.reviewandroid;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.widget.Button;import csu.lzw.reviewandroid.annotation.LBindContentView;import csu.lzw.reviewandroid.annotation.LBindView;import csu.lzw.reviewandroid.annotation.LViewBindUtils;@LBindContentView(layoutId = R.layout.activity_main)public class MainActivity extends AppCompatActivity {    private static final String TAG="MainActivity";    @LBindView(id=R.id.toggleButton)    private Button tg;    @LBindView(id = R.id.toggleButton2)    private Button tg2;    @LBindView(id = R.id.toggleButton3)    private Button tg3;    @LBindView(id = R.id.toggleButton4)    private Button tg4;    @LBindView(id = R.id.toggleButton5)    private Button tg5;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        LViewBindUtils.inject(this);        tg.setText("test");        tg2.setText("test2");    }}

关键的实现在于这一步

LViewBindUtils.inject(this);

这一步里做了什么呢


对于初始化view的注解,那么获取this指向的Activity中的成员,检查成员中是否有自定义的注解,如果有的话,获取注解中声明的id,使用反射,获取Activity的findviewbyid方法,使用invode调用,并将返回值通过反射赋值给将要初始化的view.


对于加载Activity布局的view,类似地,检查activity类上有没有自定义的注解,如果有的话,通过反射,调用setcontentview.


package csu.lzw.reviewandroid.annotation;import java.lang.reflect.Field;import java.lang.reflect.Method;import android.app.Activity;import android.util.Log;public class LViewBindUtils{private static final String METHOD_SET_CONTENTVIEW = "setContentView";private static final String METHOD_FIND_VIEW_BY_ID = "findViewById";public static void inject(Activity activity){injectContentView(activity);injectViews(activity);// injectEvents(activity);}/** * 注入所有的view控件 *  * @param activity */private static void injectViews(Activity activity){Class<? extends Activity> clazz = activity.getClass();Field[] fields = clazz.getDeclaredFields();// 遍历所有成员变量for (Field field : fields){LBindView viewInjectAnnotation = field.getAnnotation(LBindView.class);if (viewInjectAnnotation != null){int viewId = viewInjectAnnotation.id();if (viewId != -1){// 初始化Viewtry{Method method = clazz.getMethod(METHOD_FIND_VIEW_BY_ID,int.class);Object resView = method.invoke(activity, viewId);field.setAccessible(true);field.set(activity, resView);} catch (Exception e){e.printStackTrace();}}}}}/** * 注入主布局文件 *  * @param activity */private static void injectContentView(Activity activity){Class<? extends Activity> clazz = activity.getClass();// 查询类上是否存在ContentView注解LBindContentView contentView = clazz.getAnnotation(LBindContentView.class);if (contentView != null)// 存在{int contentViewLayoutId = contentView.layoutId();try{Method method = clazz.getMethod(METHOD_SET_CONTENTVIEW,int.class);method.setAccessible(true);method.invoke(activity, contentViewLayoutId);} catch (Exception e){e.printStackTrace();}}}}



0 0