一、代理模式的介绍
定义:为其他的对象提供一种代理,控制这个对象的访问。
使用场景:当无法或者不想直接访问某个对象后者访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。
角色划分:目标接口、目标对象、代理对象
实现方式:静态代理、动态代理
在我们的平时开发中,所使用的一些开源框架也有应用,如 XUtils 框架、Retrofit 框架等,Android 系统源码中也有大量应用,如
ActivityManagerProxy 代理类,对于 MVP 架构设计、插件化架构设计,代理模式更显神通。下面,通过仿照XUtils IOC 实现方式进行对代理模式的深入研究。
二、XUtils 框架分析
XUtils 主要通过注解的方式进行 UI,资源和事件绑定,下面主要对 XUtils
中 ViewUtils 模块IOC框架的实现进行分析。
IOC实现哪些功能?
第一个功能:布局文件注入
第一步:新建一个布局注解
第二步:注入布局第二个功能:View注入
第一步:新建一个View注解
第二步:注入View第三个功能:事件注入
第一步:新建一个事件注解
第二步:注入事件
XUtils动态代理角色:
目标接口:监听器(例如:OnClickListener、OnLongClickListener等等...)
目标对象:View(Button、TextView等等...)
代理对象:代码proxy对象(Proxy.newProxyInstance创建返回的对象,就是我们的代理对象)->本质:就是对方法监听
1.布局文件注入注解
//类注解
//Target:作用目标->作用在类身上(ElementType.TYPE)
@Target(ElementType.TYPE)
//Retention:生命周期->运行时注解(RetentionPolicy.RUNTIME)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView {
//布局ID
int value();
}
2.View注入注解
//Target:作用目标->作用在属性身上(ElementType.FIELD)
@Target(ElementType.FIELD)
//Retention:生命周期->运行时注解(RetentionPolicy.RUNTIME)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
//View的ID
int value();
}
3.事件注入注解
//动态指定事件
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Event {
int[] value();
Class> type() default View.OnClickListener.class;
String setter() default "";
String method() default "";
}
3.1注解事件的管理类
public class EventListenerManager {
private static DynamicHandler dynamicHandler;
private EventListenerManager() {
}
public static void addEventMethod_2_0(
Annotation eventAnnotation, Object handler, Method method, Object view) {
try {
if (view != null) {
EventBase eventBase = eventAnnotation.annotationType()
.getAnnotation(EventBase.class);
// 监听类型:OnClickListener、OnTouchListener、OnLongClickListener等等......
Class> listenerType = eventBase.listenerType();
// 事件源(你要给那个View绑定监听,而且该监听对应的方法)
// View.setOnClickListener() View.setOnTouchListener
// View.setOnLongClickListener
String listenerSetter = eventBase.listenerSetter();
// 监听方法: onClick方法、onTouch、onLongClick方法
String methodName = eventBase.methodName();
// 从缓存中获取
// 提高了性能,节约内存
Object proxy = null;
// 第一次添加监听
dynamicHandler = new DynamicHandler(handler);
dynamicHandler.addMethod(methodName, method);
// proxy:代理对象
proxy = Proxy.newProxyInstance(
listenerType.getClassLoader(),
new Class>[]{listenerType}, dynamicHandler);
// 绑定监听
Method setEventListenerMethod = view.getClass().getMethod(
listenerSetter, listenerType);
setEventListenerMethod.invoke(view, proxy);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
public static void addEventMethod_3_0(
Event event, Object handler, Method method, Object view) {
try {
if (view != null && event != null) {
// 监听类型:OnClickListener、OnTouchListener、OnLongClickListener等等......
Class> listenerType = event.type();
// 事件源(你要给那个View绑定监听,而且该监听对应的方法)
// View.setOnClickListener() View.setOnTouchListener
// View.setOnLongClickListener
String listenerSetter = event.setter();
// 监听方法: onClick方法、onTouch、onLongClick方法
String methodName = event.method();
// 从缓存中获取
// 提高了性能,节约内存
Object proxy = null;
// 第一次添加监听
dynamicHandler = new DynamicHandler(handler);
dynamicHandler.addMethod(methodName, method);
// proxy:代理对象
proxy = Proxy.newProxyInstance(
listenerType.getClassLoader(),
new Class>[]{listenerType}, dynamicHandler);
// 绑定监听
Method setEventListenerMethod = view.getClass().getMethod(
listenerSetter, listenerType);
setEventListenerMethod.invoke(view, proxy);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
// WeakReference?为什么?
// 第一点:及时清理内存
// 第二点:Activity很有可能会被意外释放(意外关闭,而这个时候你刚好执行代码到了控件的加载)
// 添加软引用目的:为了防止对象意外被释放关闭而产生异常(典型:空指针异常)
public static class DynamicHandler implements InvocationHandler {
private WeakReference
4.最终注解的工具类
public class InjectUtils {
public static void inject(Object obj) {
injectLayout(obj);
Map viewMap = injectView(obj);
// injectEvent_2_0(obj, viewMap);
injectEvent_3_0(obj, viewMap);
}
public static void injectLayout(Object obj) {
// 获取Activity的ContentView的注解
Class> handlerType = obj.getClass();
try {
//获取类对象身上的注解
ContentView contentView = handlerType.getAnnotation(ContentView.class);
if (contentView != null) {
//获取布局ID
int viewId = contentView.value();
if (viewId > 0) {
Method setContentViewMethod = handlerType.getMethod(
"setContentView", int.class);
setContentViewMethod.invoke(obj, viewId);
}
}
} catch (Throwable ex) {
ex.printStackTrace();
}
}
public static Map injectView(Object handler) {
Map viewMap = new HashMap();
//获取类对象
Class> handlerType = handler.getClass();
//获取对象属性列表
Field[] fields = handlerType.getDeclaredFields();
//判定是否存在属性
if (fields != null && fields.length > 0) {
//遍历属性
for (Field field : fields) {
//判断属性修饰符
Class> fieldType = field.getType();
if (
/* 不注入静态字段 */Modifier.isStatic(field.getModifiers()) ||
/* 不注入final字段 */Modifier.isFinal(field.getModifiers()) ||
/* 不注入基本类型字段(int、double、float、char、boolean等等...) */fieldType.isPrimitive() ||
/* 不注入数组类型字段 */fieldType.isArray()) {
continue;
}
//获取属性注解
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if (viewInject != null) {
try {
//获取ViewID
int viewId = viewInject.value();
//获取findViewById方法对象
Method findViewByIdMethod = handlerType.getMethod(
"findViewById", int.class);
//执行findViewById方法,获取对象
Object view = findViewByIdMethod.invoke(handler,viewId);
if (view != null) {
//修改访问权限(private)
//setAccessible:将属性修饰符修改为public
field.setAccessible(true);
//赋值
field.set(handler, view);
viewMap.put(viewId, view);
} else {
throw new RuntimeException(
"Invalid @ViewInject for "
+ handlerType.getSimpleName() + "."
+ field.getName());
}
} catch (Throwable ex) {
ex.printStackTrace();
}
}
}
}
return viewMap;
}
//2.0版本->实现
public static void injectEvent_2_0(Object obj, Map viewMap){
//获取类对象
Class> handlerType = obj.getClass();
//获取对象方法->activity
Method[] methods = handlerType.getDeclaredMethods();
if (methods != null && methods.length > 0) {
//遍历方法
for (Method method : methods) {
//获取方法注解
Annotation[] annotations = method.getDeclaredAnnotations();
if (annotations != null && annotations.length > 0) {
//遍历注解目的:为了获取我们想要的注解对象
for (Annotation annotation : annotations) {
//获取注解身上注解
//获取注解类型
Class> annType = annotation.annotationType();
if (annType.getAnnotation(EventBase.class) != null) {
method.setAccessible(true);
try {
//获取注解value方法
Method valueMethod = annType.getDeclaredMethod("value");
//获取OnClick、OnLongClick等等....注解身上的value方法
//values说白了就是控件的id数组
int[] values = (int[]) valueMethod.invoke(annotation);
//遍历id数组
for (int i = 0; i < values.length; i++) {
int viewId = values[i];
Object view = viewMap.get(viewId);
//对事件进行动态代理
EventListenerManager.addEventMethod_2_0(annotation, obj, method, view);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
}
}
}
}
//3.0版本->实现
public static void injectEvent_3_0(Object handler, Map viewMap){
//获取类对象
Class> handlerType = handler.getClass();
//获取对象方法->activity
Method[] methods = handlerType.getDeclaredMethods();
if (methods != null && methods.length > 0) {
for (Method method : methods) {
// 注意:静态方法不允许添加控件注解,私有方法运行访问,非私有方法不允许访问
// 在XUtils框架3.0之后,要求我们的方法必须是私有方法(注意:public不行)
// 希望该方法配置了注解,不希望子类继承,只有当前类可以享受
if (Modifier.isStatic(method.getModifiers())) {
continue;
}
// 检查当前方法是否是event注解的方法
Event event = method.getAnnotation(Event.class);
if (event != null) {
try {
// id参数
int[] values = event.value();
// 循环所有id,生成ViewInfo并添加代理反射
for (int i = 0; i < values.length; i++) {
int valueId = values[i];
if (valueId > 0) {
Object view = viewMap.get(valueId);
// ViewInfo info = new ViewInfo();
// 不管你再怎么样,永远都会创建对象
method.setAccessible(true);
EventListenerManager.addEventMethod_3_0(event, handler, method, view);
}
}
} catch (Throwable ex) {
ex.printStackTrace();
}
}
}
}
}
}
三、使用
@ContentView(R.layout.activity_main)
public class MainActivity extends BaseActivity {
@ViewInject(R.id.bt_1)
private Button bt_1;
@ViewInject(R.id.bt_2)
private Button bt_2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InjectUtils.inject(this);
}
//2.0版本->写法
// @OnClick({R.id.tv_text, R.id.tv_text_a})
// public void click(View v){
// Toast.makeText(this,"点击了TextView", Toast.LENGTH_LONG).show();
// }
//3.0版本->写法
@Event(
value = {R.id.bt_1, R.id.bt_2},
type = View.OnClickListener.class,
setter = "setOnClickListener",
method = "onClick")
public void click(View v) {
if (v.getId() == R.id.bt_1) {
Toast.makeText(this, "点击了Button->1", Toast.LENGTH_LONG).show();
} else if (v.getId() == R.id.bt_2) {
Toast.makeText(this, "点击了Button->2", Toast.LENGTH_LONG).show();
}
}
}
四、总结
对于代理模式,我认为需要不断的实践,代码是最好的老师,多去运用在自己的项目当中去,最后,附上我以前写的一个IOC注解框架,也是使用注解反射结合XUtils和ButterKnife实现方式,可拓展的开源框架,感兴趣的可以去瞧瞧,地址:Vegen的Github:自己打造IOC注解框架 ,觉得不错给个star鼓励下嘻嘻。