前言:如下是通過對注解架構的閱讀和了解,自個整理的一些筆記,想學習注解原理的朋友可以參考下。
建議:如果使用AS開發,需要用到注解,建議安裝Android ButterKnife Zelezny插件,可以提高開發效率。(注意:AS預設有butterknife的jar包,使用該插件時需要先導入butterknife包才能使用,就像導入jar一樣的操作)
總結:
- 這裡注解架構和xUtils注解架構同理,我們可以自己封裝起來,友善使用
- 注解事件是不需要控件對象的,其實在注解事件裡面已經有擷取控件對象的操作,并通過代理對象給控件對象設定了事件監聽
- 注解事件還是需要布局視圖的,不然沒地方觸發事件
- 下面代碼其實是完整的架構代碼,後面把控件的注解的注釋去掉即可,這裡是為了友善說明注解事件
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;
}
}