目前市场上比较火的Android注解框架有butterknife,dagger等,IOC的核心是解耦,修改耦合对象时不影响另外一个对象,降低模块之间的关联。IOC注解的优缺点:
1.代码量少,加速开发
2.产生性能消耗
开发过程中,经常会使用findviewById的方式去实例化一个控件的id,比如获取一个Button的id
Button btn = findviewById(R.id.btn);
如果你学会了注解的方式,就可以这样实现
@InjectView(R.id.btn)
Button btn
本文主要从三个方面讲解如何实现IOC注解功能:
1、布局注入
原理:通过放射,获得类→布局注解→注解的值→获取指定方法→执行方法,具体的步骤如下:
- 新建一个布局注解Annotation,代码如下
/**
* @author zhengjun
* @desc 布局的注解
* @create at 2019/3/22 16:26
*/
@Target(ElementType.TYPE) //该注解作用在类上
@Retention(RetentionPolicy.RUNTIME) //运行时通过反射获取该注解的值
public @interface ContentView {
int value();
}
- 实现注入过程,获得类→布局注解→注解的值→获取指定方法→执行方法
//注入布局layout
private static void injectLayout(Activity activity) {
//获取类
Class<? extends Activity> aClass = activity.getClass();
//获取类的注解
ContentView contentView = aClass.getAnnotation(ContentView.class);
if (contentView != null) {
//获取当前类的布局R.layout.activity_main
int layoutId = contentView.value();
//1.第一种方法注解,直接调用setContentView方法
//activity.setContentView(layoutId);
try {
//2.第二种方法注解,通过反射机制
Method method = aClass.getMethod("setContentView", int.class);
method.invoke(activity, layoutId);
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 在activity中使用此注解
用@ContentView(R.layout.activity_main)的方式代替setContentView()方法
2、控件注入
原理:通过放射,获得类→获取所有属性→遍历属性→每个属性的注解→每个注解的→获取指定方法→执行方法→设置访问私有→赋值
- 新建一个控件注解Annotation,代码如下
/**
* @author zhengjun
* @desc view的注解
* @create at 2019/3/22 16:54
*/
@Target(ElementType.FIELD) //该注解作用在属性上
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView {
int value();
}
- 实现注入过程,获得类→获取所有属性→遍历属性→每个属性的注解→每个注解的→获取指定方法→执行方法→设置访问私有→赋值
//控件注入
private static void injectViews(Activity activity) {
//获取类
Class<? extends Activity> aClass = activity.getClass();
//获取类所有的属性
Field[] fields = aClass.getDeclaredFields();
//遍历属性
for (Field filed : fields) {
//获取属性上的注解
InjectView injectView = filed.getAnnotation(InjectView.class);
if (injectView != null) { //并不是每个属性都有这个注解,所有要做非空判断
//获取这个属性注解的值R.id.btn
int viewId = injectView.value();
try {
//获取方法
Method method = aClass.getMethod("findViewById", int.class);
//执行方法,得到view对象
Object view = method.invoke(activity, viewId);
//设置改属性可以访问,哪怕是private类型
filed.setAccessible(true);
//属性的值赋给控件,在当前的activity
filed.set(activity, view);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
- 在activity中使用此注解
用@InjectView(R.id.btn)的方式代替findViewById()方法
3、事件注入
原理:通过放射,获得类→获取所有方法→遍历方法→每个方法的注解→遍历所有注解→获取指定的注解(类型,判空)→获取最终注解的三大成员→获得最终注解的值→遍历注解的值→赋值控件→控件获得指定方法→执行方法
- 新建一个布局注解Annotation,代码如下
/**
* @author zhengjun
* @desc
* @create at 2019/3/25 09:53
*/
@Target(ElementType.METHOD) //该注解作用在方法上
@Retention(RetentionPolicy.RUNTIME)
@EventBase(listenerSet = "setOnClickListener", listenerType = View.OnClickListener.class, callBackListener = "onClick")
public @interface OnClick {
int[] value();
}
- 新建一个作用在注解上的注解
/**
* @author zhengjun
* @desc
* @create at 2019/3/25 10:34
*/
@Target(ElementType.ANNOTATION_TYPE) //元注解 作用在注解之上
@Retention(RetentionPolicy.RUNTIME)
public @interface EventBase {
//事件的三个重要成员
//1.setOnxxxListener方法名
String listenerSet();
//2.监听的对象 View.OnxxxListener()
Class<?> listenerType();
//3.回调方法
String callBackListener();
}
- 新建一个类实现InvocationHandler实现方法的拦截
/**
* @author zhengjun
* @desc
* @create at 2019/3/25 11:06
*/
public class ListenerInvocationHandler implements InvocationHandler {
//需要拦截mainactivity中的某些方法
private Object target;
//拦截的键值对
private HashMap<String, Method> methodHashMap = new HashMap<>();
public ListenerInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (target != null) {
String name = method.getName();
//重新赋值,拦截了onClick方法
method = methodHashMap.get(name);
//如果找到了 就执行自定义的onClick方法
if (method != null) {
return method.invoke(target, args);
}
}
return null;
}
/**
* @desc 拦截方法
* methodName 要拦截的方法 onClick
* method 自定义要执行的方法
* @author zhengjun
* @created at 2019/3/25 11:12
*/
public void addMethod(String methodName, Method method) {
methodHashMap.put(methodName, method);
}
}
- 实现注入过程,获得类→获取所有方法→遍历方法→每个方法的注解→遍历所有注解→获取指定的注解(类型,判空)→获取最终注解的三大成员→获得最终注解的值→遍历注解的值→赋值控件→控件获得指定方法→执行方法
//事件注入
private static void injectEvents(Activity activity) {
//获取当前类
Class<? extends Activity> aClass = activity.getClass();
//获取所有方法
Method[] declaredMethods = aClass.getDeclaredMethods();
//遍历方法
for (Method method : declaredMethods) {
//获取方法注解,可能一个方法会有多个注解
Annotation[] annotations = method.getAnnotations();
//遍历注解
for (Annotation annotation : annotations) {
//获取注解类型
Class<? extends Annotation> annotationType = annotation.annotationType();
if (annotationType != null) {
EventBase eventBase = annotationType.getAnnotation(EventBase.class);
//事件三个重要成员
String listenerSet = eventBase.listenerSet();
Class<?> listenerType = eventBase.listenerType();
String callBackListener = eventBase.callBackListener();
try {
//通过annotationType获取onClick注解的value值
Method valueMethod = annotationType.getDeclaredMethod("value");
//R.id.tv R.id.btn
int[] viewIds = (int[]) valueMethod.invoke(annotation);
ListenerInvocationHandler invocationHandler = new ListenerInvocationHandler(activity);
invocationHandler.addMethod(callBackListener, method);
Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, invocationHandler);
for (int viewId : viewIds) {
//得到当前控件view
View view = activity.findViewById(viewId);
Method setter = view.getClass().getMethod(listenerSet, listenerType);
setter.invoke(view, listener);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
- 在activity中使用此注解
用@OnClick({R.id.btn,R.id.tv})的方式代替setOnclickListener()方法
最后,贴出整个注解过程的代码,全部封装到InjectManager类中,可以在activity的基类中调用InjectManager.init(this)方法进行注入,代码如下:
package me.zhengjun.injectview;
import android.app.Activity;
import android.view.View;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import me.zhengjun.injectview.annotation.ContentView;
import me.zhengjun.injectview.annotation.EventBase;
import me.zhengjun.injectview.annotation.InjectView;
import me.zhengjun.injectview.listener.ListenerInvocationHandler;
/**
* @author zhengjun
* @desc 注入的管理类
* @create at 2019/3/22 16:26
*/
public class InjectManager {
public static void inject(Activity activity) {
//布局注入
injectLayout(activity);
//控件注入
injectViews(activity);
//事件注入
injectEvents(activity);
}
//注入布局layout
private static void injectLayout(Activity activity) {
//获取类
Class<? extends Activity> aClass = activity.getClass();
//获取类的注解
ContentView contentView = aClass.getAnnotation(ContentView.class);
if (contentView != null) {
//获取当前类的布局R.layout.activity_main
int layoutId = contentView.value();
//1.第一种方法注解,直接调用setContentView方法
//activity.setContentView(layoutId);
try {
//2.第二种方法注解,通过反射机制
Method method = aClass.getMethod("setContentView", int.class);
method.invoke(activity, layoutId);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//控件注入
private static void injectViews(Activity activity) {
//获取类
Class<? extends Activity> aClass = activity.getClass();
//获取类所有的属性
Field[] fields = aClass.getDeclaredFields();
//遍历属性
for (Field filed : fields) {
//获取属性上的注解
InjectView injectView = filed.getAnnotation(InjectView.class);
if (injectView != null) { //并不是每个属性都有这个注解,所有要做非空判断
//获取这个属性注解的值R.id.btn
int viewId = injectView.value();
try {
//获取方法
Method method = aClass.getMethod("findViewById", int.class);
//执行方法,得到view对象
Object view = method.invoke(activity, viewId);
//设置改属性可以访问,哪怕是private类型
filed.setAccessible(true);
//属性的值赋给控件,在当前的activity
filed.set(activity, view);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
//事件注入
private static void injectEvents(Activity activity) {
//获取当前类
Class<? extends Activity> aClass = activity.getClass();
//获取所有方法
Method[] declaredMethods = aClass.getDeclaredMethods();
//遍历方法
for (Method method : declaredMethods) {
//获取方法注解,可能一个方法会有多个注解
Annotation[] annotations = method.getAnnotations();
//遍历注解
for (Annotation annotation : annotations) {
//获取注解类型
Class<? extends Annotation> annotationType = annotation.annotationType();
if (annotationType != null) {
EventBase eventBase = annotationType.getAnnotation(EventBase.class);
//时间三个重要成员
String listenerSet = eventBase.listenerSet();
Class<?> listenerType = eventBase.listenerType();
String callBackListener = eventBase.callBackListener();
try {
//通过annotationType获取onClick注解的value值
Method valueMethod = annotationType.getDeclaredMethod("value");
//R.id.tv R.id.btn
int[] viewIds = (int[]) valueMethod.invoke(annotation);
ListenerInvocationHandler invocationHandler = new ListenerInvocationHandler(activity);
invocationHandler.addMethod(callBackListener, method);
Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, invocationHandler);
for (int viewId : viewIds) {
//得到当前控件view
View view = activity.findViewById(viewId);
Method setter = view.getClass().getMethod(listenerSet, listenerType);
setter.invoke(view, listener);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}