我们自己来实现一套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. Django F()表达式

    Django F()表达式 一个F()对象代表一个模型字段的值或注释列.使用它可以直接引用模型字段的值并执行数据库操作而不用把它们导入到python的内存中. 相反,Django使用F()对象生成一个 ...

  2. (转)kafka实战教学

    转载自:https://www.cnblogs.com/hei12138/p/7805475.html Apache kafka 工作原理介绍-----https://www.ibm.com/deve ...

  3. 计算机网络 之 TCP和UDP的端口号解析

    前言:今天了解一下tcp和udp报文的端口.发现一直以来都只是知道端口用于区分同一IP的服务器的不同服务,已经端口的大小.在查找traceroute的资料的时候,才了解到一些之前没注意到的东西. (一 ...

  4. 对动态加载javascript脚本的研究

    有时我们需要在javascript脚本中创建js文件,那么在javascript脚本中创建的js文件又是如何执行的呢?和我们直接在HTML页面种写一个script标签的效果是一样的吗?(关于页面scr ...

  5. es-07-head插件-ik分词器插件

    5.x以后, es对head插件的支持并不是特别好 而且kibana的功能越来越强大, 建议使用kibana 1, head插件安装 在一台机器上安装head插件就可以了 1), 更新,安装依赖 su ...

  6. Solidity的delete操作

    Solidity中有个特殊的操作符delete用于释放空间,因为区块链技术做为一种公用资源,为避免大家滥用.且鼓励主动对空间的回收,释放空间将会返还一些gas. delete关键字的作用是对某个类型值 ...

  7. Xcode中的Vim--XVim

    相信大家在开始使用一门编程语言的时候,最先做的事就是去学习它对应的IDE.在你稍微进阶的时候,就会想着去学习IDE中的各种快捷键,比如 如何删除整行 如何在不使用鼠标的情况下,在代码间快速移动.跳转 ...

  8. Deep learning with Python 学习笔记(2)

    本节介绍基于Keras的CNN 卷积神经网络接收形状为 (image_height, image_width, image_channels)的输入张量(不包括批量维度),宽度和高度两个维度的尺寸通常 ...

  9. RocketMQ专题1:入门

    RocketMQ入门 源码和应用下载 ​ 这里以RocketMQ的4.3.0版本为例,本地环境为windows10,jdk1.8, maven3.2.1. 源码下载地址: http://mirrors ...

  10. Spring学习之路-SpringBoot简单入门

    简单讲SpringBoot是对spring和springMVC的二次封装和整合,新添加了一些注解和功能,不算是一个新框架. 学习来源是官方文档,虽然很详细,但是太墨迹了… 地址:https://doc ...