天天看點

Android注解架構IOC(設定事件)

前言:如下是通過對注解架構的閱讀和了解,自個整理的一些筆記,想學習注解原理的朋友可以參考下。

建議:如果使用AS開發,需要用到注解,建議安裝Android ButterKnife Zelezny插件,可以提高開發效率。(注意:AS預設有butterknife的jar包,使用該插件時需要先導入butterknife包才能使用,就像導入jar一樣的操作)

總結:

  1. 這裡注解架構和xUtils注解架構同理,我們可以自己封裝起來,友善使用
  2. 注解事件是不需要控件對象的,其實在注解事件裡面已經有擷取控件對象的操作,并通過代理對象給控件對象設定了事件監聽
  3. 注解事件還是需要布局視圖的,不然沒地方觸發事件
  4. 下面代碼其實是完整的架構代碼,後面把控件的注解的注釋去掉即可,這裡是為了友善說明注解事件

getDeclaredMethod()和getMethod()的差別:

getDeclaredMethod*()擷取的是類自身聲明的所有方法,包含public、protected和private方法。

getMethod*()擷取的是類的所有公有方法,這就包括自身的所有public方法,和從基類繼承的、從接口實作的所有public方法。

注意:getDeclaredMethod方法對自身的類有完整的通路權限,但針對目前類;getMethod方法隻對public的方法有通路權限,由于是公有,那麼對自身的,基類繼承的,接口實作的方法都有通路權限

主Activity中(注意:即使處理的是控件的注解事件,也要加入布局,可以直接加入或者注解加入)

@ContentView(value = R.layout.activity_main)
public class MainActivity extends Activity {
//	@ViewInject(R.id.id_btn)
//	private Button mBtn1;
//	@ViewInject(R.id.id_btn02)
//	private Button mBtn2;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		ViewInjectUtils.inject(this);
	}
	@OnClick({ R.id.id_btn, R.id.id_btn02 })
	public void clickBtnInvoked(View view) {
		switch (view.getId()) {
		case R.id.id_btn:
			Toast.makeText(this, "Inject Btn01 !", Toast.LENGTH_SHORT).show();
			break;
		case R.id.id_btn02:
			Toast.makeText(this, "Inject Btn02 !", Toast.LENGTH_SHORT).show();
			break;
		}
	}
	
}
           

主界面的注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView {
	int value();//@ContentView(value = R.layout.activity_main)
}
           

onClick的注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@EventBase(listenerType = View.OnClickListener.class, listenerSetter = "setOnClickListener", methodName = "onClick")
public @interface OnClick {
	int[] value();//{ R.id.id_btn, R.id.id_btn02 } 注入到這個int數組中
}
           

onClick注解上的@EventBase注解:

@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventBase {
	Class<?> listenerType();//監聽類型,泛型---》View.OnClickListener.class
	String listenerSetter();//設定監聽的方法---》setOnClickListener
	String methodName();//監聽回調方法名---》onClick
}
           

注解主要的處理類:

/**
 * 主要處理類
 * @author huangweicai
 *
 */
public class ViewInjectUtils {
	private static final String METHOD_SET_CONTENTVIEW = "setContentView";
	private static final String METHOD_FIND_VIEW_BY_ID = "findViewById";
	public static void inject(Activity activity) {
		injectContentView(activity);//設定主界面視圖
//		injectViews(activity);//給控件注入資源對象(這裡不需要)
		injectEvents(activity);//事件處理
	}
	/**
	 * 注入所有的事件
	 * @param activity
	 */
	private static void injectEvents(Activity activity) {
		//繼承Activity的子類的反射
		Class<? extends Activity> clazz = activity.getClass();
		Method[] methods = clazz.getMethods();//擷取所有方法,用方法數組接收
		// 周遊方法數組,得到每一個方法的注解
		for (Method method : methods) {
			//也用注解數組接收,因為一個方法上可能不止一個注解
			Annotation[] annotations = method.getAnnotations();
			// 取得注解數組中的每一個注解
			for (Annotation annotation : annotations) {
				//得到注解的注解類型對象,該對象可以擷取注解上的注解
				Class<? extends Annotation> annotationType = annotation.annotationType();
				// 拿到注解上的注解
				EventBase eventBaseAnnotation = annotationType.getAnnotation(EventBase.class);
				// eventBaseAnnotation存在
				if (eventBaseAnnotation != null) {
					//注意:注解對象是可以直接點出來其中的成員對象的(預設修飾符:protect)
					//setOnClickListener
					String listenerSetter = eventBaseAnnotation.listenerSetter();
					//View.OnClickListener.class
					Class<?> listenerType = eventBaseAnnotation.listenerType();
					//onClick
					String methodName = eventBaseAnnotation.methodName();
					try {
						//得到MainActivity中注解值為value的方法
						Method aMethod = annotationType.getDeclaredMethod("value");//目前類所有方法(包括公有、私有)
						//執行該方法并傳入注解對象,傳回注入值的數組,{ R.id.id_btn, R.id.id_btn02 }
						int[] viewIds = (int[]) aMethod.invoke(annotation, null);
						// 通過InvocationHandler設定代理
						DynamicHandler handler = new DynamicHandler(activity);
						// 往haspmap集合中,添加需要攔截的事件方法,通過“循環+判斷”得到的事件方法名來指定:methodName
						handler.addMethod(methodName, method);
						
						// 建立listenertype的代理對象-----
						Object listener = Proxy.newProxyInstance(
								listenerType.getClassLoader(),//View.OnClickListener.class對象擷取類加載器
								new Class<?>[] { listenerType },
								handler
								);
						
						// 周遊所有的View,設定事件
						for (int viewId : viewIds) {
							//得到資源對象
							View view = activity.findViewById(viewId);//傳入資源id
							//這步就是:setOnClickListener(new View.OnClickListener.class)
							Method setEventListenerMethod = view.getClass()
									.getMethod(listenerSetter, listenerType);
							//這步就是:view通過代理對象來執行設定監聽
							setEventListenerMethod.invoke(view, listener);//listener 代理對象傳進來
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
	/**
	 * 注入所有的控件
	 * @param activity
	 */
	private static void injectViews(Activity activity) {
		Class<? extends Activity> clazz = activity.getClass();
		Field[] fields = clazz.getFields();
		// 周遊所有成員變量
		for (Field field : fields) {
			ViewInject viewInjectAnnotation = field
					.getAnnotation(ViewInject.class);
			if (viewInjectAnnotation != null) {
				int viewId = viewInjectAnnotation.value();
				if (viewId != -1) {
					// 初始化View
					try {
						Method method = clazz.getMethod(METHOD_FIND_VIEW_BY_ID,
								Integer.class);
						Object resView = method.invoke(clazz, viewId);
						field.setAccessible(true);
						field.set(clazz, resView);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
	/**
	 * 注入主布局檔案
	 * @param activity
	 */
	private static void injectContentView(Activity activity) {
		Class<? extends Activity> clazz = activity.getClass();
		// 查詢類上是否存在ContentView注解
		ContentView contentView = clazz.getAnnotation(ContentView.class);
		if (contentView != null)// 存在
		{
			int contentViewLayoutId = contentView.value();
			try {
				Method method = clazz.getMethod(METHOD_SET_CONTENTVIEW,
						int.class);
				method.setAccessible(true);
				method.invoke(activity, contentViewLayoutId);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}
           

攔截代理類:

/**
 * 代理攔截類
 * @author huangweicai
 *
 */
public class DynamicHandler implements InvocationHandler {
	private WeakReference<Object> handlerRef;//弱引用
	private final HashMap<String, Method> methodMap = new HashMap<String, Method>(1);
	
	/**
	 * 構造函數
	 * @param handler
	 */
	public DynamicHandler(Object handler) {
		this.handlerRef = new WeakReference<Object>(handler);
	}
	
	/**
	 * 添加攔截的方法到對應清單
	 * @param handler
	 */
	public void addMethod(String name, Method method) {
		methodMap.put(name, method);
	}
	
	public Object getHandler() {
		return handlerRef.get();
	}
	
	public void setHandler(Object handler) {
		this.handlerRef = new WeakReference<Object>(handler);
	}
	
	//執行攔截回調
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		Object handler = handlerRef.get();//攔截到的onClick方法
		if (handler != null) {
			String methodName = method.getName();//得到方法名,即onClick
			method = methodMap.get(methodName);//得到該方法名的方法對象
			if (method != null) {
				return method.invoke(handler, args);
			}
		}
		return null;
	}
}
           
Android注解架構IOC(設定事件)

繼續閱讀