一、代理模式的介紹
定義:為其他的對象提供一種代理,控制這個對象的訪問。
使用場景:當無法或者不想直接訪問某個對象后者訪問某個對象存在困難時可以通過一個代理對象來間接訪問,為了保證客戶端使用的透明性,委托對象與代理對象需要實現相同的接口。
角色劃分:目標接口、目標對象、代理對象
實現方式:靜態代理、動態代理
在我們的平時開發中,所使用的一些開源框架也有應用,如 XUtils 框架、Retrofit 框架等,Android 系統源碼中也有大量應用,如
ActivityManagerProxy 代理類,對于 MVP 架構設計、插件化架構設計,代理模式更顯神通。下面,通過仿照XUtils IOC 實現方式進行對代理模式的深入研究。
二、XUtils 框架分析
XUtils 主要通過注解的方式進行 UI,資源和事件綁定,下面主要對 XUtils
中 ViewUtils 模塊IOC框架的實現進行分析。
IOC實現哪些功能?
第一個功能:布局文件注入
第一步:新建一個布局注解
第二步:注入布局第二個功能:View注入
第一步:新建一個View注解
第二步:注入View第三個功能:事件注入
第一步:新建一個事件注解
第二步:注入事件
XUtils動態代理角色:
目標接口:監聽器(例如:OnClickListener、OnLongClickListener等等...)
目標對象:View(Button、TextView等等...)
代理對象:代碼proxy對象(Proxy.newProxyInstance創建返回的對象,就是我們的代理對象)->本質:就是對方法監聽
1.布局文件注入注解
//類注解
//Target:作用目標->作用在類身上(ElementType.TYPE)
@Target(ElementType.TYPE)
//Retention:生命周期->運行時注解(RetentionPolicy.RUNTIME)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView {
//布局ID
int value();
}
2.View注入注解
//Target:作用目標->作用在屬性身上(ElementType.FIELD)
@Target(ElementType.FIELD)
//Retention:生命周期->運行時注解(RetentionPolicy.RUNTIME)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
//View的ID
int value();
}
3.事件注入注解
//動態指定事件
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Event {
int[] value();
Class<?> type() default View.OnClickListener.class;
String setter() default "";
String method() default "";
}
3.1注解事件的管理類
public class EventListenerManager {
private static DynamicHandler dynamicHandler;
private EventListenerManager() {
}
public static void addEventMethod_2_0(
Annotation eventAnnotation, Object handler, Method method, Object view) {
try {
if (view != null) {
EventBase eventBase = eventAnnotation.annotationType()
.getAnnotation(EventBase.class);
// 監聽類型:OnClickListener、OnTouchListener、OnLongClickListener等等......
Class<?> listenerType = eventBase.listenerType();
// 事件源(你要給那個View綁定監聽,而且該監聽對應的方法)
// View.setOnClickListener() View.setOnTouchListener
// View.setOnLongClickListener
String listenerSetter = eventBase.listenerSetter();
// 監聽方法: onClick方法、onTouch、onLongClick方法
String methodName = eventBase.methodName();
// 從緩存中獲取
// 提高了性能,節約內存
Object proxy = null;
// 第一次添加監聽
dynamicHandler = new DynamicHandler(handler);
dynamicHandler.addMethod(methodName, method);
// proxy:代理對象
proxy = Proxy.newProxyInstance(
listenerType.getClassLoader(),
new Class<?>[]{listenerType}, dynamicHandler);
// 綁定監聽
Method setEventListenerMethod = view.getClass().getMethod(
listenerSetter, listenerType);
setEventListenerMethod.invoke(view, proxy);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
public static void addEventMethod_3_0(
Event event, Object handler, Method method, Object view) {
try {
if (view != null && event != null) {
// 監聽類型:OnClickListener、OnTouchListener、OnLongClickListener等等......
Class<?> listenerType = event.type();
// 事件源(你要給那個View綁定監聽,而且該監聽對應的方法)
// View.setOnClickListener() View.setOnTouchListener
// View.setOnLongClickListener
String listenerSetter = event.setter();
// 監聽方法: onClick方法、onTouch、onLongClick方法
String methodName = event.method();
// 從緩存中獲取
// 提高了性能,節約內存
Object proxy = null;
// 第一次添加監聽
dynamicHandler = new DynamicHandler(handler);
dynamicHandler.addMethod(methodName, method);
// proxy:代理對象
proxy = Proxy.newProxyInstance(
listenerType.getClassLoader(),
new Class<?>[]{listenerType}, dynamicHandler);
// 綁定監聽
Method setEventListenerMethod = view.getClass().getMethod(
listenerSetter, listenerType);
setEventListenerMethod.invoke(view, proxy);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
// WeakReference?為什么?
// 第一點:及時清理內存
// 第二點:Activity很有可能會被意外釋放(意外關閉,而這個時候你剛好執行代碼到了控件的加載)
// 添加軟引用目的:為了防止對象意外被釋放關閉而產生異常(典型:空指針異常)
public static class DynamicHandler implements InvocationHandler {
private WeakReference<Object> handlerRef;
private final HashMap<String, Method> methodMap = new HashMap<String, Method>(
1);
// 目標對象: Activity、Fragment
public DynamicHandler(Object handler) {
this.handlerRef = new WeakReference<Object>(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();
if (handler != null) {
String methodName = method.getName();
method = methodMap.get(methodName);
// 為什么做判斷?
// 目的:確定代理要代理的方法
if (method != null) {
return method.invoke(handler, args);
}
}
return null;
}
}
}
4.最終注解的工具類
public class InjectUtils {
public static void inject(Object obj) {
injectLayout(obj);
Map<Integer, Object> viewMap = injectView(obj);
// injectEvent_2_0(obj, viewMap);
injectEvent_3_0(obj, viewMap);
}
public static void injectLayout(Object obj) {
// 獲取Activity的ContentView的注解
Class<?> handlerType = obj.getClass();
try {
//獲取類對象身上的注解
ContentView contentView = handlerType.getAnnotation(ContentView.class);
if (contentView != null) {
//獲取布局ID
int viewId = contentView.value();
if (viewId > 0) {
Method setContentViewMethod = handlerType.getMethod(
"setContentView", int.class);
setContentViewMethod.invoke(obj, viewId);
}
}
} catch (Throwable ex) {
ex.printStackTrace();
}
}
public static Map<Integer, Object> injectView(Object handler) {
Map<Integer, Object> viewMap = new HashMap<Integer, Object>();
//獲取類對象
Class<?> handlerType = handler.getClass();
//獲取對象屬性列表
Field[] fields = handlerType.getDeclaredFields();
//判定是否存在屬性
if (fields != null && fields.length > 0) {
//遍歷屬性
for (Field field : fields) {
//判斷屬性修飾符
Class<?> fieldType = field.getType();
if (
/* 不注入靜態字段 */Modifier.isStatic(field.getModifiers()) ||
/* 不注入final字段 */Modifier.isFinal(field.getModifiers()) ||
/* 不注入基本類型字段(int、double、float、char、boolean等等...) */fieldType.isPrimitive() ||
/* 不注入數組類型字段 */fieldType.isArray()) {
continue;
}
//獲取屬性注解
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if (viewInject != null) {
try {
//獲取ViewID
int viewId = viewInject.value();
//獲取findViewById方法對象
Method findViewByIdMethod = handlerType.getMethod(
"findViewById", int.class);
//執行findViewById方法,獲取對象
Object view = findViewByIdMethod.invoke(handler,viewId);
if (view != null) {
//修改訪問權限(private)
//setAccessible:將屬性修飾符修改為public
field.setAccessible(true);
//賦值
field.set(handler, view);
viewMap.put(viewId, view);
} else {
throw new RuntimeException(
"Invalid @ViewInject for "
+ handlerType.getSimpleName() + "."
+ field.getName());
}
} catch (Throwable ex) {
ex.printStackTrace();
}
}
}
}
return viewMap;
}
//2.0版本->實現
public static void injectEvent_2_0(Object obj, Map<Integer, Object> viewMap){
//獲取類對象
Class<?> handlerType = obj.getClass();
//獲取對象方法->activity
Method[] methods = handlerType.getDeclaredMethods();
if (methods != null && methods.length > 0) {
//遍歷方法
for (Method method : methods) {
//獲取方法注解
Annotation[] annotations = method.getDeclaredAnnotations();
if (annotations != null && annotations.length > 0) {
//遍歷注解目的:為了獲取我們想要的注解對象
for (Annotation annotation : annotations) {
//獲取注解身上注解
//獲取注解類型
Class<?> annType = annotation.annotationType();
if (annType.getAnnotation(EventBase.class) != null) {
method.setAccessible(true);
try {
//獲取注解value方法
Method valueMethod = annType.getDeclaredMethod("value");
//獲取OnClick、OnLongClick等等....注解身上的value方法
//values說白了就是控件的id數組
int[] values = (int[]) valueMethod.invoke(annotation);
//遍歷id數組
for (int i = 0; i < values.length; i++) {
int viewId = values[i];
Object view = viewMap.get(viewId);
//對事件進行動態代理
EventListenerManager.addEventMethod_2_0(annotation, obj, method, view);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
}
}
}
}
//3.0版本->實現
public static void injectEvent_3_0(Object handler, Map<Integer, Object> viewMap){
//獲取類對象
Class<?> handlerType = handler.getClass();
//獲取對象方法->activity
Method[] methods = handlerType.getDeclaredMethods();
if (methods != null && methods.length > 0) {
for (Method method : methods) {
// 注意:靜態方法不允許添加控件注解,私有方法運行訪問,非私有方法不允許訪問
// 在XUtils框架3.0之后,要求我們的方法必須是私有方法(注意:public不行)
// 希望該方法配置了注解,不希望子類繼承,只有當前類可以享受
if (Modifier.isStatic(method.getModifiers())) {
continue;
}
// 檢查當前方法是否是event注解的方法
Event event = method.getAnnotation(Event.class);
if (event != null) {
try {
// id參數
int[] values = event.value();
// 循環所有id,生成ViewInfo并添加代理反射
for (int i = 0; i < values.length; i++) {
int valueId = values[i];
if (valueId > 0) {
Object view = viewMap.get(valueId);
// ViewInfo info = new ViewInfo();
// 不管你再怎么樣,永遠都會創建對象
method.setAccessible(true);
EventListenerManager.addEventMethod_3_0(event, handler, method, view);
}
}
} catch (Throwable ex) {
ex.printStackTrace();
}
}
}
}
}
}
三、使用
@ContentView(R.layout.activity_main)
public class MainActivity extends BaseActivity {
@ViewInject(R.id.bt_1)
private Button bt_1;
@ViewInject(R.id.bt_2)
private Button bt_2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InjectUtils.inject(this);
}
//2.0版本->寫法
// @OnClick({R.id.tv_text, R.id.tv_text_a})
// public void click(View v){
// Toast.makeText(this,"點擊了TextView", Toast.LENGTH_LONG).show();
// }
//3.0版本->寫法
@Event(
value = {R.id.bt_1, R.id.bt_2},
type = View.OnClickListener.class,
setter = "setOnClickListener",
method = "onClick")
public void click(View v) {
if (v.getId() == R.id.bt_1) {
Toast.makeText(this, "點擊了Button->1", Toast.LENGTH_LONG).show();
} else if (v.getId() == R.id.bt_2) {
Toast.makeText(this, "點擊了Button->2", Toast.LENGTH_LONG).show();
}
}
}
四、總結
對于代理模式,我認為需要不斷的實踐,代碼是最好的老師,多去運用在自己的項目當中去,最后,附上我以前寫的一個IOC注解框架,也是使用注解反射結合XUtils和ButterKnife實現方式,可拓展的開源框架,感興趣的可以去瞧瞧,地址:Vegen的Github:自己打造IOC注解框架 ,覺得不錯給個star鼓勵下嘻嘻。