1.了解反射相关的Api
getFields() // 获取所有的public属性
getDeclaredFields() // 获取所有的属性包括private属性
getMethods() // 获取所有的public方法
getDeclaredMethods() // 获取所有的方法包括private方法
getAnnotation(Class<A> annotationClass) // 获取参数类型的注解对象
invoke(Object obj, Object... args) // 传递object对象及参数调用该对象对应的方法
2.项目目录
inject目录下包含有一个annotation文件夹和一个InjectManager类 annotation目录存放自定的注解,InjectManager实现具体的功能

3.自定义注解
//ElementType.TYPE:作用在类上
//ElementType.FIELD:作用在属性上
//ElementType.METHOD:作用在方法上
// 注入layout自定义注解
@Target(ElementType.TYPE) // 定义注解的类型 这是一个作用在类的上注解
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectLayout {
int value(); // 注解传进来的参数,int类型:R.layout.xxx
}
// 注入View自定义注解
@Target(ElementType.FIELD) // 定义注解的类型 这是一个作用在属性的上注解
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView {
int value(); // 注解传进来的参数,int类型:R.id.xxx
}
4.InjectManager类实现
import android.app.Activity;
import com.ruiheng.injectdemo.inject.annotation.InjectLayout;
import com.ruiheng.injectdemo.inject.annotation.InjectView;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class InjectManager {
public static void injectLayout(Activity activity) {
Class<? extends Activity> clazz = activity.getClass();
//获取当前类上的InjectLayout注解
InjectLayout injectLayout = clazz.getAnnotation(InjectLayout.class);
if (injectLayout != null) { //如果当前类上有InjectLayout注解
//获取layoutId
int layoutId = injectLayout.value();
try {
//通过getMethod获取setContentView方法
Method method = clazz.getMethod("setContentView", int.class);
//执行setContentView操作
method.invoke(activity, layoutId);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void injectView(Activity activity) {
Class<? extends Activity> clazz = activity.getClass();
//通过反射获取所有属性
// clazz.getFields()只能获取public的属性,包括父类的属性
// clazz.getDeclaredFields()能获取所有属性包括private的属性
Field[] fields = clazz.getDeclaredFields();
//遍历所有属性
for (Field field : fields) {
InjectView annotation = field.getAnnotation(InjectView.class);
if (annotation != null) {
try {
//获取findView方法
Method findViewById = clazz.getMethod("findViewById", int.class);
//通过findView方法获取View
Object view = findViewById.invoke(activity, annotation.value());
//打开私有权限
field.setAccessible(true);
//给属性设置值
field.set(activity, view);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
5.使用
可以在activity的基类中统一调用
InjectManager.injectLayout(this);
InjectManager.injectView(this);

在子类activity使用注解注入

6.总结
昨天通过公开课学习手撸butterknife类似效果,今天自己手撸一遍,做个记录。
整体实现是通过反射+注解的方式实现,跟butterknife的实现方式还不太一样(butterknife是通过apt+注解,编译器编译时自动生成辅助类)。
OS:一直不喜欢用butterKnife,觉得findViewById和@BindView在代码量上也没啥差别(都是一行嘛。。。)