XUtils2.0中我们Android有多少个监听,你就要定义多少个注解类
注解类数量非常庞大?---问题:框架也就变得越来越臃肿
XUtils3.0之后整体架构大的改进和升级
ViewUtils模块为例:
第一个优化改进:整体架构(采用面向接口编程同时采用一些设计模式)
第二个优化改进:ViewUtils事件注解结构类进行改进(由用户配置)
将所有注解事件进行了合并,由用户指定对应的监听
默认是OnClickListener监听
性能提高了(之前是嵌套循环)
整体架构如图:
具体代码如下:
主布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
tools:context="${relativePackage}.${activityClass}" >
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="15dp"
android:layout_margin="3dp"
android:layout_centerInParent="true"
android:gravity="center"
android:text="hello_world"
android:textColor="#ffff00"
android:background="@android:color/black"/>
</RelativeLayout>
package com.architect.proxydynamic.android
package com.architect.proxydynamic.android;
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.architect.proxydynamic.android.libs1.InjectUtils;
import com.architect.proxydynamic.android.libs1.event.ContentView;
import com.architect.proxydynamic.android.libs1.event.Event;
import com.architect.proxydynamic.android.libs1.event.ViewInject;
import com.tz.architect.proxydynamic.android.R;
@ContentView(R.layout.activity_main)
public class MainActivity extends Activity {
@ViewInject(R.id.tv_title)
private TextView tv_title;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InjectUtils.inject(this);
tv_title.setText("依赖注入");
}
@Event(listenerType = View.OnTouchListener.class, value = { R.id.tv_title })
private boolean touch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Toast.makeText(this, "触摸事件产生", Toast.LENGTH_LONG).show();
break;
default:
break;
}
return true;
}
}
package com.architect.proxydynamic.android.libs1
package com.architect.proxydynamic.android.libs1;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import android.app.Activity;
import android.text.TextUtils;
import android.view.View;
import com.architect.proxydynamic.android.libs1.event.ContentView;
import com.architect.proxydynamic.android.libs1.event.Event;
import com.architect.proxydynamic.android.libs1.event.ViewInject;
/**
* 注入
*
* @author Dream
*
*/
public class InjectUtils {
/**
* 注入Activity
*
* @param activity
*/
public static void inject(Activity activity) {
injectLayout(activity);
Map<Integer, View> injectView = injectView(activity);
injectEvent(activity, injectView);
}
/**
* 注入布局
*
* @param activity
*/
private static void injectLayout(Activity activity) {
// 获取activit类对象
Class<? extends Activity> clazz = activity.getClass();
// 获取Activity类上的指定的注解ContentView
ContentView contentView = clazz.getAnnotation(ContentView.class);
if (contentView != null) {
// 加载布局
// 获取Activity身上setContentView方法,然后执行
try {
Method setContentViewMethod = clazz.getMethod("setContentView",
int.class);
// 执行Activity身上的方法
setContentViewMethod.invoke(activity, contentView.value());
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 注入视图
*
* @param activity
*/
private static Map<Integer, View> injectView(Activity activity) {
Map<Integer, View> viewMap = new HashMap<Integer, View>();
Class<? extends Activity> clazz = activity.getClass();
// 获取当前Activity身上所有的属性
Field[] declaredFields = clazz.getDeclaredFields();
try {
for (Field field : declaredFields) {
// 获取注入视图注解
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if (viewInject != null) {
int viewId = viewInject.value();
View view = activity.findViewById(viewId);
// 绑定
field.setAccessible(true);
field.set(activity, view);
viewMap.put(viewId, view);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return viewMap;
}
/**
* 注入事件
*
* @param activity
* @param injectView
*/
private static void injectEvent(Activity activity,
Map<Integer, View> injectView) {
// 分析
// 思考:事件分为三要素?(事件源、回调监听、处理函数)
// 事件源:
// View.setOnClickListener()、View.setOnLongClickListener()、View.setOnTouchListener()
// 说白了就是View本身
// 回调监听:
// OnClickListener、OnLongClickListener、OnTouchListener事件回调监听等等......
// 处理函数: onClick、onLongClick、onTouch
// 第一步:定义事件三要素(注解)(注意:事件三要素基类是作用在注解上)
// 第二步:定义具体的事件(OnClickListener、OnLongClickListener)
// 第三步:注入控件
// 1、首先获取目标对象所有的方法
Class<? extends Activity> clazz = activity.getClass();
Method[] methods = clazz.getDeclaredMethods();
// 2、遍历所有的方法,筛选需要被代理的方法
try {
for (Method method : methods) {
if (Modifier.isStatic(method.getModifiers())
|| !Modifier.isPrivate(method.getModifiers())) {
continue;
}
// 注意:记得加上方法访问权限(因为我们做了私有处理)
method.setAccessible(true);
Event event = method.getAnnotation(Event.class);
if (event != null) {
int[] viewIds = event.value();
Class<?> listenerType = event.listenerType();
String listenerSetter = event.listenerSetter();
//优化,自动拼接set方法
if (TextUtils.isEmpty(listenerSetter)) {
listenerSetter = "set" + listenerType.getSimpleName();
}
String callbackMethod = event.callbackMethod();
System.out.println(" callbackMethod = " + callbackMethod);
ListenerInvocationHandler invocationHandler = new ListenerInvocationHandler(
activity);
// callbackMethod:代表是View的监听方法
// method:代表是目标对象的方法(说白了就是Activity中定义的方法)
// 代理方法和目标方法一一对应
invocationHandler.addMethod(callbackMethod, method);
// 7.绑定监听(通过动态代理实现)
// listener:代理对象
Object listener = Proxy.newProxyInstance(
clazz.getClassLoader(),
new Class[] { listenerType }, invocationHandler);
for (int viewId : viewIds) {
View view = injectView.get(viewId);
// 绑定监听(获取的方法代表:setOnXXXListener()方法对象)
Method listenerMethod = view.getClass().getMethod(
listenerSetter, listenerType);
// 执行方法
listenerMethod.invoke(view, listener);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.architect.proxydynamic.android.libs1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import android.text.TextUtils;
public class ListenerInvocationHandler implements InvocationHandler {
// 目标对象
private Object targetObj;
// 需要代理方法
private Map<String, Method> methodMap = new HashMap<String, Method>();
public ListenerInvocationHandler(Object targetObj) {
this.targetObj = targetObj;
}
public void addMethod(String methodName, Method method) {
this.methodMap.put(methodName, method);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 处理需要代理方法
if (targetObj != null) {
// 获取需要代理的方法
// 说明你没有指定方法
method = this.methodMap.get(method.getName());
System.out.println("method = "+method + " --- methodMap = "+methodMap);
// 以下代码就是优化
if (method == null && methodMap.size() == 1) {
for (Map.Entry<String, Method> entry : methodMap.entrySet()) {
if (TextUtils.isEmpty(entry.getKey())) {
method = entry.getValue();
}
break;
}
}
System.out.println(" 111 method = "+method );
if (method != null) {
return method.invoke(targetObj, args);
}
}
return null;
}
}
package com.architect.proxydynamic.android.libs1.even
package com.architect.proxydynamic.android.libs1.event;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//@Target:作用范围
//ElementType.TYPE:该注解作用在类身上
@Target(ElementType.TYPE)
// @Retention:注解生命周期(这个注解什么时候运行加载)
// RetentionPolicy.RUNTIME:在的虚拟机运行的时候加载运行
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView {
//布局文件ID
int value();
}
package com.architect.proxydynamic.android.libs1.event;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import android.view.View;
//作用方法
@Target(ElementType.METHOD)
// 运行时加载注解
@Retention(RetentionPolicy.RUNTIME)
public @interface Event {
// 定义事件三要素
// 定义事件源
// 指定默认值:""
String listenerSetter() default "";
// 定义回调监听
// 指定默认值为:View.OnClickListener.class
Class<?> listenerType() default View.OnClickListener.class;
// 定义处理函数
// 指定默认值:""
String callbackMethod() default "";
// 加了一个要素(指定事件对应的View的id)
int[] value();
}
package com.architect.proxydynamic.android.libs1.event;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//ElementType.FIELD:代表作用在属性身上
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
//View id
int value();
}
运行效果图:
整理自示例源码