Ioc注入框架的手寫實現
目的:自己手寫是爲了進一步加深Android種IOC注入實現的理解,例如Android Annotations,ButterKnife,Dagger等開源庫都有體現。
代碼參考:https://github.com/itlgc/Android-IOC-Custom
loC的核心是解耦
在Spring中IoC更多的是依靠xml的配置
而Android上的IoC框架可以不使用xml配置
佈局注入思路
獲得類>佈局註解>註解的值>獲取指定方法>執行方法
Class<? extends Activity> clazz = activity.getClass();
//獲取類之上的註解
ContentView contentView = clazz.getAnnotation(ContentView.class);
if (contentView != null) {
//獲取註解的值,也就是佈局id
int layoutId = contentView.value();
//執行setContentView方法
Method setContentViewMethod = clazz.getMethod("setContentView", int.class);
setContentViewMethod.invoke(activity, layoutId);
}
控件注入思路
獲得類>獲得所有屬性>遍歷屬性>每個屬性的註解>每個註解的值>獲得指定方法>執行方法>設置訪問私有>賦值
Class<? extends Activity> clazz = activity.getClass();
//獲取該類所有屬性
Field[] fields = clazz.getDeclaredFields();
//循環 獲取屬性上的註解
for (Field field : fields) {
InjectView injectView = field.getAnnotation(InjectView.class);
if (injectView != null) {
int viewId = injectView.value();
Method findViewByIdMethod = clazz.getMethod("findViewById", int.class);
Object view = findViewByIdMethod.invoke(activity, viewId);
//賦值 屬性的值賦值給控件 在當前的Activity
field.setAccessible(true);
field.set(activity, view);
}
}
事件注入思路
事件有點擊事件、長按事件、觸摸事件等,需要對它們進行兼容處理
/**
* 代碼特點:
* 1、setXXXClickListener
* 2、xxxClickListener
* 3、回調方法 onxxx()
*/
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//真正寫代碼的地方
}
});
btn.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return false;
}
});
根據事件代碼特點將其3個重要的成員進行封裝
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventBase {
//事件3個重要的成員封裝
// 1、setXXXClickListener
String listenerSetter();
// 2、new xxxClickListener()對象
Class<?> listenerType();
// 3、回調方法 onxxx()
String callbackListener();
}
針對點擊事件的註解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@EventBase(listenerSetter = "setOnClickListener",listenerType = View.OnClickListener.class,
callbackListener = "onClick")
public @interface OnClick {
int[] value();
}
具體操作思路見如下注釋
Class<? extends Activity> clazz = activity.getClass();
//獲取類的所有方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
//循環 獲取方法上的所有註解
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
//遍歷 獲取其中onClick註解上的註解,即獲取父類EventBase註解上的類型
// 進而從方法諸多註解中篩選出Onclick註解
Class<? extends Annotation> annotationType = annotation.annotationType();
if (annotationType != null) {
//獲取EventBase註解 和事件3個重要的成員封裝
EventBase eventBase = annotationType.getAnnotation(EventBase.class);
if (eventBase != null) {
String listenerSetter = eventBase.listenerSetter();
Class<?> listenerType = eventBase.listenerType();
//這就是需要攔截的方法
String callbackListener = eventBase.callbackListener();
//獲取onClick註解中的value方法
Method valueMethod = annotationType.getDeclaredMethod("value");
valueMethod.setAccessible(true);
//獲取註解上的值 (數組)
int[] ViewsId = (int[]) valueMethod.invoke(annotation);
//動態代理
ListenerInvocationHandler invocationHandler =
new ListenerInvocationHandler(activity);
invocationHandler.addMethods(callbackListener, method);
Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(),
new Class[]{listenerType}, invocationHandler);
for (int viewId : ViewsId) {
//獲取當前activity中View控件 並賦值
View view = activity.findViewById(viewId);
//這樣才能兼容 setxxxClicklister setxxxLongClickLister等
Method setter = view.getClass().getMethod(listenerSetter, listenerType);
setter.invoke(view, listener);
}
}
}
}
}