动态代理-事件注入(一)
1.目的:
通过代理的方式去注册控件的点击事件,长按事件等其他事件。
- 优点:隔离代码层,解耦
- 缺点:运行时反射的效率低
2.第一版本(先不用动态代理):
2.1 创建一个注解类 (用来区分方法是否需要注册点击事件)
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface OnClick { //接收控件的ID int[] value() default -1;}
2.2 创建一个注解工具类
public class InjectUtil { public static void init(Object content) { //1.获取类中所有的方法 Method[] methods = content.getClass().getMethods(); for (Method method : methods) { //2.找到有标注注解的方法 OnClick onClick = method.getAnnotation(OnClick.class); if (onClick != null) { int[] value = onClick.value(); try { //3.通过反射获取findViewById方法 Method findViewById = content.getClass().getMethod("findViewById", int.class); for (int i : value) { //4.获取控件 View view = (View) findViewById.invoke(content, i); if (view != null) {//5.设置监听view.setOnClickListener(v -> { try { //6.方法回调 method-> 就是我们外面标注注解的方法 method.invoke(content, v); } catch (IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); }}); } } } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } } }}
2.3 注册工具类
BaseActivity
类
public class BaseActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); InjectUtil.init(this); }}
MainActivity
类
public class MainActivity extends BaseActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { setContentView(R.layout.activity_main); super.onCreate(savedInstanceState); } @OnClick(R.id.btn) public void start(View view) { Toast.makeText(this,"我被点击了",Toast.LENGTH_SHORT).show(); }}
注意:MainActivity类中,setContentView必须在super.onCreate()方法前,因为只有在setContentView后控件才会被创建。
很简单的代码,但这样会产生一个问题;
1.那就是如果你还要注册长按事件,viewpager的滑动事件,那是不是还得继续往文件写代码,违背了类的单一原则,也不好扩展,
2.你如何区分哪个控件需要长按,哪个不需要?按照下面的写法,所有的控件都会被自动注册长按事件,
如下长按事件;
view.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { //6.方法回调 method-> 就是我们外面标注注解的方法 try { //6.方法回调 method-> 就是我们外面标注注解的方法 method.invoke(content, v); } catch (IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } return false; }});
接下来我们用动态代理看下,能不能解决上面的问题
3.使用动态代理:
我们简单的修改下InjectUtil
类
public static void init(Object content) { //1.获取类中所有的方法 Method[] methods = content.getClass().getMethods(); for (Method method : methods) { //2.找到有标注注解的方法 OnClick onClick = method.getAnnotation(OnClick.class); if (onClick != null) { int[] value = onClick.value(); try { //3.通过反射获取findViewById方法 Method findViewById = content.getClass().getMethod("findViewById", int.class); for (int i : value) { //4.获取控件 View view = (View) findViewById.invoke(content, i); //5.通过代理获取View.OnClickListener View.OnClickListener listener = (View.OnClickListener) Proxy.newProxyInstance(content.getClass().getClassLoader(), new Class[]{View.OnClickListener.class}, new OnClickInvocationHandler(content,method)); //6.设置监听 view.setOnClickListener(listener); } } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } }}
OnClickInvocationHandler
类
public class OnClickInvocationHandler implements InvocationHandler { private final Object content;//可以是Activity,fragment,dialog private final Method method; public OnClickInvocationHandler(Object content, Method method) { this.content = content; this.method = method; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (content != null) { //这里的回调是 setOnClickListener的回调 //method 也就是start方法 this.method.invoke(content,args); } return null; }}
动态代理用上了,但好像也没解决问题,只不过是换了个高级点的用法,屁用没有。
问题依旧存在,如下:
1.那就是如果你还要注册长按事件,viewpager的滑动事件,那是不是还得继续往文件写代码,违背了类的单一原则,也不好扩展,
2.你如何区分哪个控件需要长按,哪个不需要?按照下面的写法,所有的控件都会被自动注册长按事件。
留到下一章解决,haha
动态代理-事件注入(二)https://blog.csdn.net/wumeixinjiazu/article/details/122499788
代码地址:IOCDemo: 动态代理ioc注解https://gitee.com/small_insects/IOCDemo