个人博客
http://www.milovetingting.cn
IOC之运行时注入-实现Activity的布局注入+控件注入+事件绑定
前言
本文主要介绍基于IOC
的设计原则,实现以下功能:
其实这些功能,在之前也有零散地介绍过,这里再做一个统一的整理。
这里暂时不考虑运行时反射的效率问题,只是展示一种实现方案。
IOC的定义
IOC,即Inversion of Control
,意为控制反转
,是面向对象编程中的一种设计原则,可以用来降低代码间的耦合。最常见的方式是依赖注入
(Dependence Injection
,简称DI
)。通过IOC,对象在创建时,由外界来控制,而不是内部直接控制。
布局注入
平时,我们在Activity中,可能会通过在onCreate
方法中调用setContentView
的方法,给Activity绑定布局。而基于IOC,则可以通过注解
来实现:
注解的定义
1 2 3 4 5 6 7 8 9 10
| @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface LayoutInject {
@LayoutRes int value(); }
|
注解的使用
1 2 3 4 5 6 7 8 9
| @LayoutInject(R.layout.activity_main) public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); InjectUtil.inject(this); } }
|
在MainActivity上加上前面定义的LayoutInject注解,然后在onCreate中调用注入的方法InjectUtil.inject(this)
Inject方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
public static void inject(Object target) { if (target == null) { return; } injectLayout(target); }
private static void injectLayout(Object target) { Class<?> clazz = target.getClass(); boolean annotationPresent = clazz.isAnnotationPresent(LayoutInject.class); if (!annotationPresent) { return; } LayoutInject annotation = clazz.getAnnotation(LayoutInject.class); int layoutId = annotation.value(); try { Method method = clazz.getMethod("setContentView", int.class); method.invoke(target, layoutId); } catch (Exception e) { e.printStackTrace(); } }
|
控件注入
注解的定义
1 2 3 4 5 6 7 8 9 10
| @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ViewInject {
@IdRes int value(); }
|
注解的使用
1 2 3 4 5
| @ViewInject(R.id.btn1) Button btn1;
@ViewInject(R.id.btn2) Button btn2;
|
Inject方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
public static void inject(Object target) { if (target == null) { return; } injectView(target); }
private static void injectView(Object target) { Class<?> clazz = target.getClass(); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { boolean annotationPresent = field.isAnnotationPresent(ViewInject.class); if (!annotationPresent) { continue; } ViewInject annotation = field.getAnnotation(ViewInject.class); int viewId = annotation.value(); try { Method method = clazz.getMethod("findViewById", int.class); View view = (View) method.invoke(target, viewId); field.setAccessible(true); field.set(target, view); } catch (Exception e) { e.printStackTrace(); } } }
|
事件注入
注解的定义
事件类型的注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Target(ElementType.ANNOTATION_TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Event {
String listenerSetter();
Class<?> listenerType(); }
|
点击事件的注解
1 2 3 4 5 6 7 8 9 10 11 12
| @Event(listenerSetter = "setOnClickListener", listenerType = View.OnClickListener.class) @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface OnClick {
@IdRes int[] value(); }
|
长按事件的注解
1 2 3 4 5 6 7 8 9 10 11 12
| @Event(listenerSetter = "setOnLongClickListener", listenerType = View.OnLongClickListener.class) @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface OnLongClick {
@IdRes int[] value(); }
|
注解的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| @OnClick({R.id.btn1, R.id.btn2}) public void click(View view) { int id = view.getId(); switch (id) { case R.id.btn1: Toast.makeText(getApplicationContext(), "按钮1点击了", Toast.LENGTH_SHORT).show(); break; case R.id.btn2: Toast.makeText(getApplicationContext(), "按钮2点击了", Toast.LENGTH_SHORT).show(); break; default: break; } }
@OnLongClick({R.id.btn1, R.id.btn2}) public boolean longClick(View view) { int id = view.getId(); switch (id) { case R.id.btn1: Toast.makeText(getApplicationContext(), "按钮1长按了", Toast.LENGTH_SHORT).show(); break; case R.id.btn2: Toast.makeText(getApplicationContext(), "按钮2长按了", Toast.LENGTH_SHORT).show(); break; default: break; } return true; }
|
Inject方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
|
public static void inject(Object target) { if (target == null) { return; } injectEvent(target); }
private static void injectEvent(Object target) { Class<?> clazz = target.getClass(); Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { Annotation[] annotations = method.getAnnotations(); for (Annotation annotation : annotations) { Class<? extends Annotation> annotationType = annotation.annotationType(); boolean annotationPresent = annotationType.isAnnotationPresent(Event.class); if (!annotationPresent) { continue; } Event event = annotationType.getAnnotation(Event.class); String listenerSetter = event.listenerSetter(); Class<?> listenerType = event.listenerType(); try { Method valueMethod = annotationType.getDeclaredMethod("value"); valueMethod.setAccessible(true); int[] viewIds = (int[]) valueMethod.invoke(annotation); for (int viewId : viewIds) { Method findViewByIdMethod = clazz.getMethod("findViewById", int.class); View view = (View) findViewByIdMethod.invoke(target, viewId); if (view == null) { continue; } ListenerInvocationHandler listenerInvocationHandler = new ListenerInvocationHandler(target, method); Object proxyInstance = Proxy.newProxyInstance(InjectUtil.class.getClassLoader(), new Class[]{listenerType}, listenerInvocationHandler); Method listenerSetterMethod = view.getClass().getMethod(listenerSetter, listenerType); listenerSetterMethod.setAccessible(true); listenerSetterMethod.invoke(view, proxyInstance); } } catch (Exception e) { e.printStackTrace(); } }
} }
static class ListenerInvocationHandler implements InvocationHandler {
private Object target;
private Method method;
public ListenerInvocationHandler(Object target, Method method) { this.target = target; this.method = method; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return this.method.invoke(target, args); } }
|