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

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

① IOC的View属性注解类:

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

②IOC 注入 ViewUtils

 public class ViewUtils {

     public static void inject(Activity activity) {
inject(new ViewFinder(activity), activity);
} // 兼容View
public static void inject(View view) {
inject(new ViewFinder(view), view);
} // 兼容Fragment
public static void inject(View view, Object object) {
inject(new ViewFinder(view), object);
} private static void inject(ViewFinder viewFinder, Object object) {
injectFiled(viewFinder, object);
injectEvent(viewFinder, object);
} // 注入事件
private static void injectEvent(ViewFinder viewFinder, Object object) { } /**
* 注入属性
*/
private static void injectFiled(ViewFinder viewFinder, Object object) {
// object --> activity or fragment or view 是反射的类
// viewFinder --> 只是一个view的findViewById的辅助类 // 1. 获取所有的属性
Class<?> clazz = object.getClass();
// 获取所有属性包括私有和公有
Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) {
// 2. 获取属性上面ViewById的值
ViewById viewById = field.getAnnotation(ViewById.class); if (viewById != null) {
// 获取ViewById属性上的viewId值
int viewId = viewById.value();
// 3. 通过findViewById获取View
View view = viewFinder.findViewById(viewId); if (view != null) {
// 4. 反射注入View属性
// 设置所有属性都能注入包括私有和公有
field.setAccessible(true);
try {
field.set(object, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} else {
throw new RuntimeException("Invalid @ViewInject for "
+ clazz.getSimpleName() + "." + field.getName());
}
}
}
}
}

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.获取方法上面的所有id
for (Method method : methods) {
OnClick onClick = method.getAnnotation(OnClick.class);
if (onClick != null) {
int[] viewIds = onClick.value();
if (viewIds.length > ) {
for (int viewId : viewIds) {
// 3.遍历所有的id 先findViewById然后 setOnClickListener
View 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;
} @Override
public 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; @Override
protected 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 = / ;
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帮助类

 public class ViewHelper {

     private Activity mActivity;
private View mView; public ViewHelper(Activity activity) {
this.mActivity = activity;
} public ViewHelper(View v) {
this.mView = v;
} public View findViewById(@IdRes int id) { return mActivity == null ? mView.findViewById(id) : mActivity.findViewById(id);
}
}

③ioc注入工具类

 public class ViewUtils {

     public static void inject(Activity activity) {
inject(new ViewHelper(activity), activity);
} //为了兼容View
public static void inject(View v) {
inject(new ViewHelper(v), v);
} //为了兼容Fragment
public static void inject(View v, Object o) {
inject(new ViewHelper(v), o);
} /**
* 最终都调用这个方法
*
* @param helper View的帮助类 通过这个类根据id找到相应View
* @param o 相关对象 Activity view 等 从那个类传进来的
*/
private static void inject(ViewHelper helper, Object o) {
injectField(helper, o);
injectMethod(helper, o);
} /**
* 通过@ViewById得到id注入相应的View
*
* @param helper View帮助类
* @param o 相关对象 Activity view 等 从那个类传进来的
*/
private static void injectField(ViewHelper helper, Object o) {
//1.获取到Object中所有的带有@ViewById的字段
Class<?> clazz = o.getClass();
//获取所有的字段
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
//2.获取到相应的value值,也就是id值得到相应的View
ViewById viewById = field.getAnnotation(ViewById.class);
if (viewById != null) {
int viewId = viewById.value();
View view = helper.findViewById(viewId);
if (view != null) {
try {
//3.设置字段值,也就是给字段赋值
field.setAccessible(true);//为了使不被修饰符梭影响
field.set(o, view);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
} /**
* 设置点击事件
*
* @param helper View帮助类
* @param o 相关对象 Activity view 等 从那个类传进来的
*/
private static void injectMethod(ViewHelper helper, Object o) {
//1.获取到所有带有@OnClick的方法
Class<?> clazz = o.getClass();
Method[] methods = clazz.getDeclaredMethods();//获取所有方法
for (Method method : methods) {
OnClick onClick = method.getAnnotation(OnClick.class);
if (onClick != null) {
//网络检测
CheckNet checkNet = method.getAnnotation(CheckNet.class);
String hint = null;
if (checkNet != null) {
hint = checkNet.value();
} //2.获取到相应的value值,也就是要设置点击时间的id数组
int[] values = onClick.value();
for (int viewId : values) {
//3.通过id获取到相应的Vie,然后设置点击事件
View view = helper.findViewById(viewId);
if (view != null) {
view.setOnClickListener(new DeclaredOnClickListener(method, o, hint));
}
}
}
}
} private static class DeclaredOnClickListener implements View.OnClickListener { //设置点击事件的方法
private Method mMethod;
//在那个类中
private Object mObject;
//是否检查网络
private String mNoNetHint; public DeclaredOnClickListener(Method method, Object o, String hint) {
this.mMethod = method;
this.mObject = o;
this.mNoNetHint = hint;
} @Override
public void onClick(View v) {
try {
mMethod.setAccessible(true);//所有修饰符都可以搞事
if (mNoNetHint != null && !isNetConnected(v.getContext())) {
Toast.makeText(v.getContext(), mNoNetHint, Toast.LENGTH_SHORT).show();
return;
}
mMethod.invoke(mObject, v);//可以避免点击闪退
} catch (Exception e) {
e.printStackTrace();
try {
mMethod.invoke(mObject, (Object[]) null);//当方法体里面没有参数时候调用改方法,执行没有方法体的修饰的函数
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
} /**
* 检测网络是否连接
*
* @return
*/
private static boolean isNetConnected(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm != null) {
NetworkInfo[] infos = cm.getAllNetworkInfo();
if (infos != null) {
for (NetworkInfo ni : infos) {
if (ni.isConnected()) {
return true;
}
}
}
}
return false;
} }

④使用

public class MainActivity extends AppCompatActivity {

    @ViewById(R.id.icon)
private ImageView mIconIv; @Override
protected 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();
}
}

自己来实现一套IOC注解框架的更多相关文章

  1. Android面试基础(一)IOC(DI)框架(ViewUtils)讲解_反射和自定义注解类

    1. Android中的IOC(DI)框架 1.1 ViewUtils简介(xUtils中的四大部分之一) IOC: Inverse of Controller 控制反转. DI: Dependenc ...

  2. Spring框架学习(6)使用ioc注解方式配置bean

    内容源自:使用ioc注解方式配置bean context层 : 上下文环境/容器环境 applicationContext.xml 1 ioc注解功能 注解 简化xml文件配置 如 hibernate ...

  3. 仿写一个简陋的 IOC/AOP 框架 mini-spring

    讲道理,感觉自己有点菜.Spring 源码看不懂,不想强行解释,等多积累些项目经验之后再看吧,但是 Spring 中的控制反转(IOC)和面向切面编程(AOP)思想很重要,为了更好的使用 Spring ...

  4. java注解框架

    我们经常会在java代码里面看到:“@Override”,“@Target”等等样子的东西,这些是什么? 在java里面它们是“注解”. 下面是百度百科的解释:java.lang.annotation ...

  5. 注解框架---AndroidAnnotations

    AndroidAnnotations是一个开源框架,旨在加快Android开发的效率.通过使用它开放出来的注解api,你差点儿可以使用在不论什么地方, 大大的降低了无关痛痒的代码量,让开发人员可以抽身 ...

  6. Android注解框架实战-ButterKnife

    文章大纲 Android注解框架介绍 ButterKnife实战 项目源码下载   一.框架介绍 为什么要用注解框架?  在Android开发过程中,我们经常性地需要操作组件,操作方法有findVie ...

  7. Android 注解框架对比

    Java的注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记,标记可以加在包,类,属性,方法,本地变量上.然后你可以写一个注解处理器去解析处理这些注解(人称编译时注解 ...

  8. ButterKnife注解框架详解

    Android 懒人注解框架 :https://github.com/JakeWharton/butterknife 前言: 注解,相信很多同学都用到了,对控件进行初始化的时候需要用到 findVie ...

  9. Spring的IOC注解开发入门1

    基本知识点如下: 引入注解约束,配置组件扫描 类上的注解: @Conponent  @Controller @Service @Repository 普通属性的注解   @value 对象属性的注解  ...

随机推荐

  1. 在MVC3中修改KindEditor实现上传图片到指定文件夹

    KindEditor编辑器默认上传的图片文件夹,是根据系统时间自动生成的,图片是自动上传到这些文件夹里面,无法选择.如果要上传图片到指定文件夹,像相册一样管理图片,则需要扩展KindEditor编辑器 ...

  2. Shell 相互调用

    Shell 文件包含 和其他语言一样,Shell 也可以包含外部脚本.这样可以很方便的封装一些公用的代码作为一个独立的文件. Shell 文件包含的语法格式如下: . filename # 注意点号( ...

  3. OAuth 2.0 安全案例回顾

    原文:http://drops.wooyun.org/papers/598 0x00 背景 纵观账号互通发展史,可以发现OAuth比起其它协议(如OpenID)更流行的原因是,业务双方不仅要求账号本身 ...

  4. Quartz.NET基础知识概述

    Quartz.NET是什么 由于我现在使用的Quartz.NET2.2版本,相对2.x变化不大,主要是相对于1.x更新了很多东西,如下基础知识摘录网络. Quartz.NET是一个开源的作业调度框架, ...

  5. Intellij IDEA Spring Boot 项目Debug模式启动缓慢问题

    问题 Intellij IDEA Spring Boot 项目Debug模式启动缓慢 环境 os: windows10 idea :2018.1 解决方法 去除所有断点就正常了,很诡异,原因未知.

  6. js实现生成中间带图片的二维码

    之前需要实现生成中间带图片的二维码,所以找了半天终于找到一个可以用的.于是在这里记录一下. 下面是需要注意的几点: 1.使用的js为jquery-qrcode 但是已经经过别人的修改,和网上原来的那些 ...

  7. [Python 从入门到放弃] 2. 列表的其它操作

    1. 列表可以混合不同类型的数据项 movies=['<黄飞鸿之英雄有梦>','<寒战2>','<乘风破浪>'] 在这句代码中,数据项为电影名称,现在这个列表用来保 ...

  8. p132代码解析

    1. long before = System.currentTimeMillis(); long after = System.currentTimeMillis(); //该两句代码规定了一个do ...

  9. 前端组件化Polymer深入篇(1)

    在前面的几节里面简单的介绍了一下Polymer的基本功能,但还有一些细节的东西并没有讨论,所有打算花点时间把Polymer的一些细节写一下. new和createElement有区别吗? <sc ...

  10. Java-Maven(八):配置远程中央仓库的各种方法

    一.远程仓库的配置 在平时的开发中,我们往往不会使用默认的中央仓库,默认的中央仓库访问的速度比较慢,访问的人或许很多,有时候也无法满足我们项目的需求,可能项目需要的某些构件中央仓库中是没有的,而在其他 ...