我们自己来实现一套IOC注解框架吧,采用的方式反射加注解和Xutils类似,但我们尽量不写那么麻烦,也不打算采用动态代理,我们扩展一个检测网络的注解,比如没网的时候我们不去执行方法而是给予没有网络的提示同时也不允许用户反复点击。
  这个时候有人就开始喷了,明知道反射会影响性能为什么还要用?这里我就随便说说吧,我承认反射会影响性能但是问题不大我们可以自己去测试反射1万次大概会怎样,如果你非得去纠结那我也没办法,我们还是多花时间在UI渲染和Bitmap以及Service和Handler上面吧,我还从来没有遇到过反射调用gc或者内存溢出的情况,而且后面讲插件化开发的时候也会用到反射那砸门就不做了?不管了开工。

1 控件属性注入
  
  先来处理控件属性的注入,但是需要考虑各种情况:

① IOC的View属性注解类:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ViewById {int value();
}

②IOC 注入 ViewUtils

 1 public class ViewUtils {
 2
 3     public static void inject(Activity activity) {
 4         inject(new ViewFinder(activity), activity);
 5     }
 6
 7     // 兼容View
 8     public static void inject(View view) {
 9         inject(new ViewFinder(view), view);
10     }
11
12     // 兼容Fragment
13     public static void inject(View view, Object object) {
14         inject(new ViewFinder(view), object);
15     }
16
17     private static void inject(ViewFinder viewFinder, Object object) {
18         injectFiled(viewFinder, object);
19         injectEvent(viewFinder, object);
20     }
21
22     // 注入事件
23     private static void injectEvent(ViewFinder viewFinder, Object object) {
24
25     }
26
27     /**
28      * 注入属性
29      */
30     private static void injectFiled(ViewFinder viewFinder, Object object) {
31         // object --> activity or fragment or view 是反射的类
32         // viewFinder --> 只是一个view的findViewById的辅助类
33
34         // 1. 获取所有的属性
35         Class<?> clazz = object.getClass();
36         // 获取所有属性包括私有和公有
37         Field[] fields = clazz.getDeclaredFields();
38
39         for (Field field : fields) {
40             // 2. 获取属性上面ViewById的值
41             ViewById viewById = field.getAnnotation(ViewById.class);
42
43             if (viewById != null) {
44                 // 获取ViewById属性上的viewId值
45                 int viewId = viewById.value();
46                 // 3. 通过findViewById获取View
47                 View view = viewFinder.findViewById(viewId);
48
49                 if (view != null) {
50                     // 4. 反射注入View属性
51                     // 设置所有属性都能注入包括私有和公有
52                     field.setAccessible(true);
53                     try {
54                         field.set(object, view);
55                     } catch (IllegalAccessException e) {
56                         e.printStackTrace();
57                     }
58                 } else {
59                     throw new RuntimeException("Invalid @ViewInject for "
60                             + clazz.getSimpleName() + "." + field.getName());
61                 }
62             }
63         }
64     }
65 }

2 点击事件注入
 
 事件的注入我们只打算setOnclickListener其他不常见的我们先不管,也不打算采用动态代理的设计模式。

①OnClick

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OnClick {int []value();
}

②ViewUtils.java

// 事件注入
private static void injectEvent(ViewFinder viewFinder, Object object) {// 1.获取所有方法Class<?> clazz = object.getClass();Method[] methods = clazz.getDeclaredMethods();// 2.获取方法上面的所有idfor (Method method : methods) {OnClick onClick = method.getAnnotation(OnClick.class);if (onClick != null) {int[] viewIds = onClick.value();if (viewIds.length > 0) {for (int viewId : viewIds) {// 3.遍历所有的id 先findViewById然后 setOnClickListenerView view = viewFinder.findViewById(viewId);if (view != null) {view.setOnClickListener(new DeclaredOnClickListener(method, object));}}}}}}private static class DeclaredOnClickListener implements View.OnClickListener {private Method mMethod;private Object mHandlerType;public DeclaredOnClickListener(Method method, Object handlerType) {mMethod = method;mHandlerType = handlerType;}@Overridepublic void onClick(View v) {// 4.反射执行方法mMethod.setAccessible(true);try {mMethod.invoke(mHandlerType, v);} catch (Exception e) {e.printStackTrace();try {mMethod.invoke(mHandlerType, null);} catch (Exception e1) {e1.printStackTrace();}}}}

③使用:

public class MainActivity extends AppCompatActivity {@ViewById(R.id.icon)private ImageView mIconIv;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ViewUtils.inject(this);mIconIv.setImageResource(R.drawable.icon);}@OnClick(R.id.icon)private void onClick(View view) {int i = 2 / 0;Toast.makeText(this, "图片点击了"+i, Toast.LENGTH_LONG).show();}
}

使用起来和xutils类似,方法和属性可以私有,但是有一点我们在Onclick点击事件的方法里面无论做什么操作都是不会报错的,所以如果发现bug需要留意警告日志,这不是坑嗲吗?其实在我们的开发过程给用户或者老板玩的时候我们最怕的是闪退,现在我们就算有Bug也不会出现闪退的情况只是调试的时候需要留意警告日志还是蛮不错的。
 
 3.3 扩展动态检测网络注解

我们最后扩展一下加一个检测网络的注解,有的时候我们在点击的方法里面需要去检测网络,比如登陆注册,我们如果没网就没必要去调接口启动线程了,只需要提示用户当前无网络即可。当然这只是一个扩展而已。

①点击之后是否要执行网络检测

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CheckNet {String value() default "亲,您的网络链接有问题哦!";
}

②View帮助类

 1 public class ViewHelper {
 2
 3     private Activity mActivity;
 4     private View mView;
 5
 6     public ViewHelper(Activity activity) {
 7         this.mActivity = activity;
 8     }
 9
10     public ViewHelper(View v) {
11         this.mView = v;
12     }
13
14     public View findViewById(@IdRes int id) {
15
16         return mActivity == null ? mView.findViewById(id) : mActivity.findViewById(id);
17     }
18 }

③ioc注入工具类

  1 public class ViewUtils {
  2
  3     public static void inject(Activity activity) {
  4         inject(new ViewHelper(activity), activity);
  5     }
  6
  7     //为了兼容View
  8     public static void inject(View v) {
  9         inject(new ViewHelper(v), v);
 10     }
 11
 12     //为了兼容Fragment
 13     public static void inject(View v, Object o) {
 14         inject(new ViewHelper(v), o);
 15     }
 16
 17
 18     /**
 19      * 最终都调用这个方法
 20      *
 21      * @param helper View的帮助类  通过这个类根据id找到相应View
 22      * @param o      相关对象  Activity  view 等  从那个类传进来的
 23      */
 24     private static void inject(ViewHelper helper, Object o) {
 25         injectField(helper, o);
 26         injectMethod(helper, o);
 27     }
 28
 29     /**
 30      * 通过@ViewById得到id注入相应的View
 31      *
 32      * @param helper View帮助类
 33      * @param o      相关对象  Activity  view 等  从那个类传进来的
 34      */
 35     private static void injectField(ViewHelper helper, Object o) {
 36         //1.获取到Object中所有的带有@ViewById的字段
 37         Class<?> clazz = o.getClass();
 38         //获取所有的字段
 39         Field[] fields = clazz.getDeclaredFields();
 40         for (Field field : fields) {
 41             //2.获取到相应的value值,也就是id值得到相应的View
 42             ViewById viewById = field.getAnnotation(ViewById.class);
 43             if (viewById != null) {
 44                 int viewId = viewById.value();
 45                 View view = helper.findViewById(viewId);
 46                 if (view != null) {
 47                     try {
 48                         //3.设置字段值,也就是给字段赋值
 49                         field.setAccessible(true);//为了使不被修饰符梭影响
 50                         field.set(o, view);
 51                     } catch (Exception e) {
 52                         e.printStackTrace();
 53                     }
 54                 }
 55             }
 56         }
 57     }
 58
 59     /**
 60      * 设置点击事件
 61      *
 62      * @param helper View帮助类
 63      * @param o      相关对象  Activity  view 等  从那个类传进来的
 64      */
 65     private static void injectMethod(ViewHelper helper, Object o) {
 66         //1.获取到所有带有@OnClick的方法
 67         Class<?> clazz = o.getClass();
 68         Method[] methods = clazz.getDeclaredMethods();//获取所有方法
 69         for (Method method : methods) {
 70             OnClick onClick = method.getAnnotation(OnClick.class);
 71             if (onClick != null) {
 72                 //网络检测
 73                 CheckNet checkNet = method.getAnnotation(CheckNet.class);
 74                 String hint = null;
 75                 if (checkNet != null) {
 76                     hint = checkNet.value();
 77                 }
 78
 79                 //2.获取到相应的value值,也就是要设置点击时间的id数组
 80                 int[] values = onClick.value();
 81                 for (int viewId : values) {
 82                     //3.通过id获取到相应的Vie,然后设置点击事件
 83                     View view = helper.findViewById(viewId);
 84                     if (view != null) {
 85                         view.setOnClickListener(new DeclaredOnClickListener(method, o, hint));
 86                     }
 87                 }
 88             }
 89         }
 90     }
 91
 92     private static class DeclaredOnClickListener implements View.OnClickListener {
 93
 94         //设置点击事件的方法
 95         private Method mMethod;
 96         //在那个类中
 97         private Object mObject;
 98         //是否检查网络
 99         private String mNoNetHint;
100
101         public DeclaredOnClickListener(Method method, Object o, String hint) {
102             this.mMethod = method;
103             this.mObject = o;
104             this.mNoNetHint = hint;
105         }
106
107         @Override
108         public void onClick(View v) {
109             try {
110                 mMethod.setAccessible(true);//所有修饰符都可以搞事
111                 if (mNoNetHint != null && !isNetConnected(v.getContext())) {
112                     Toast.makeText(v.getContext(), mNoNetHint, Toast.LENGTH_SHORT).show();
113                     return;
114                 }
115                 mMethod.invoke(mObject, v);//可以避免点击闪退
116             } catch (Exception e) {
117                 e.printStackTrace();
118                 try {
119                     mMethod.invoke(mObject, (Object[]) null);//当方法体里面没有参数时候调用改方法,执行没有方法体的修饰的函数
120                 } catch (Exception e1) {
121                     e1.printStackTrace();
122                 }
123             }
124         }
125     }
126
127
128     /**
129      * 检测网络是否连接
130      *
131      * @return
132      */
133     private static boolean isNetConnected(Context context) {
134         ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
135         if (cm != null) {
136             NetworkInfo[] infos = cm.getAllNetworkInfo();
137             if (infos != null) {
138                 for (NetworkInfo ni : infos) {
139                     if (ni.isConnected()) {
140                         return true;
141                     }
142                 }
143             }
144         }
145         return false;
146     }
147
148 }

④使用

public class MainActivity extends AppCompatActivity {@ViewById(R.id.icon)private ImageView mIconIv;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ViewUtils.inject(this);mIconIv.setImageResource(R.drawable.icon);}@OnClick(R.id.icon)@CheckNet          // 检测网络private void onClick(View view) {Toast.makeText(this, "图片点击了", Toast.LENGTH_LONG).show();}
}

转载于:https://www.cnblogs.com/ganchuanpu/p/8182767.html

自己来实现一套IOC注解框架相关推荐

  1. 红橙Darren视频笔记 IOC注解框架 自己写个注解框架

    本节是架构篇开篇 这里先解释一下最终项目的架构 目标 目标就是不需要进行一大堆的findviewbyid以及各种setListener的杂乱代码,本文以findviewbyid和setOnClickL ...

  2. 红橙Darren视频笔记 IOC注解框架 了解xUtils3与ButterKnife的原理

    1.什么是IOC IOC是Inversion of Control的缩写,直接翻译过来就叫依赖反转,看起来感觉不明觉厉,我觉得IOC就是一种解耦方式.比如原本我们在Activity中findviewb ...

  3. .Net转Java自学之路—Spring框架篇二(IOC注解、AOP)

    Spring的Bean操作(注解方式): 注解:代码中特殊的标记,使用注解可以完成相关功能. @注解名称(属性名称=属性值) 注解可以使用在类.方法.属性上面. 主要用途:可以替代配置文件来创建对象和 ...

  4. IOC注解注入View

    1.简介 IOC注解注入View模块非常简单,主要是使用注解@InjectView的方式注入XML中的View组件.以免重复调用findViewById这个方法.使代码更加整洁.----------s ...

  5. 开发自己的山寨Android注解框架

    目录 开发自己的山寨Android注解框架 开发自己的山寨Android注解框架 参考 Github黄油刀 Overview 在上一章我们学习了Java的注解(Annotation),但是我想大家可能 ...

  6. 基于ASP.Net Core开发的一套通用后台框架

    基于ASP.Net Core开发一套通用后台框架 写在前面 这是本人在学习的过程中搭建学习的框架,如果对你有所帮助那再好不过.如果您有发现错误,请告知我,我会第一时间修改. 知其然,知其所以然,并非重 ...

  7. Spring Ioc注解式开发中注解的作用

    Spring Ioc注解式开发中注解的作用 目标:本章节注重于Spring Ioc的注解式开发之中注解的作用. 基于注解的DI实现IOC 1.定义bean而注解 a.@Component @Repos ...

  8. Spring IOC注解开发

    Spring IOC注解开发 @(Spring)[Spring, ioc, 注解] Spring IOC注解开发 Spring的IOC的注解使用步骤 创建项目引入jar包 引入配置文件 创建相关包和类 ...

  9. android编译时注解,Android编译时注解框架系列2-Run Demo

    概述 先讲一下编写<Android编译时注解框架>的初衷吧,APT其实并不难,可以说是简单且高效,但关于APT的资料却并不多,甚至很多人都不知道这么一个技术.国内关于APT的博客屈指可数, ...

最新文章

  1. 大学python用什么教材-Python大学教程(普通高等教育十三五规划教材)
  2. 根据传递函数仿真模拟滤波器的波特图(持续更新中)
  3. 【Error-Android Studio】clang++: error: no such file or directory
  4. 【C语言进阶深度学习记录】二十五 指针与数组的本质分析二
  5. javafx应用启动自动执行函数_JavaFx:Application start方法中的异常
  6. 字典,和字典的增删改查
  7. 从零开始学Vue(一)—— Vue.js 入门
  8. Gradle删除本地库文件
  9. c语言编程界面优化输出图形,C语言编程实例—输出指定图形
  10. Hive基础教程 Hive入门
  11. ubuntu下查看硬件配置
  12. PMP知识点:项目经理必备的11种人际关系技能
  13. flv视频合并(vs2008)
  14. 校验非空的注解@NotNull怎么取得自定义的message
  15. java七牛获取访问路径_七牛回调及回调鉴权
  16. 共享充电,是雪中送炭还是暗藏危险?——恶意充电宝实验
  17. Java中的switch
  18. 计算机期末总结ppt课件,计算机期末总结
  19. CVPR 2016 摘要阅读
  20. [nk] 糟糕的打谱员 线性DP

热门文章

  1. 1996.游戏中的弱角色的数量
  2. fscapture设置中文_Fscapture是什么软件?有没有功能介绍?
  3. 20211130:力扣第267周周赛(下)
  4. 20191011:冒泡排序的改良版--Shaker排序
  5. trackbar控件显示刻度值_安卓自定义电平流图形控件
  6. Linux 内核源代码的结构
  7. VB 获取路径名各部分 (获取文件路径,获取文件名,获取文件扩展名)自编
  8. TensorFlow手把手教你概率编程:TF Probability内置了开源教材,新手友好
  9. 北大朱占星:顶级会议疯狂投稿下的AI现状 | 专栏
  10. 纳什叫上林书豪,投了一家AI篮球训练公司