Android开发 打造自己的Annotation框架

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

之前在项目开发过程中,也曾经体验过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)
				{
					// 初始化View
					try
					{
						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();
			}
		}
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值