基于动态代理仿写的一个类似XUtils 3.0的ioc注解模块

XUtils2.0中我们Android有多少个监听,你就要定义多少个注解类

注解类数量非常庞大?---问题:框架也就变得越来越臃肿

 

XUtils3.0之后整体架构大的改进和升级

ViewUtils模块为例:

第一个优化改进:整体架构(采用面向接口编程同时采用一些设计模式)

第二个优化改进:ViewUtils事件注解结构类进行改进(由用户配置)

       将所有注解事件进行了合并,由用户指定对应的监听

       默认是OnClickListener监听

       性能提高了(之前是嵌套循环)


在XUtils框架3.0之后,要求我们的方法必须是私有方法(注意:public不行)


整体架构如图:


具体代码如下:

主布局:

<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();
}

运行效果图:





整理自示例源码




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值