我们自己来实现一套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. Redis学习系列五Set(集合)

    一.简介 Redis中的Set(集合)相当于C#中的HashSet,它内部的键值对时无序的.唯一的.用过Dictionary的都知道,Dictionary都知道,里面的每个键值对肯定是唯一的,因为键不 ...

  2. salesforce 零基础学习(七十)使用jquery tree实现树形结构模式

    项目中UI需要用到树形结构显示内容,后来尽管不需要做了,不过还是自己做着玩玩,mark一下,免得以后项目中用到. 实现树形结构在此使用的是jquery的dynatree.js.关于dynatree的使 ...

  3. Jmeter报错之jmeter.gui.action.ActionRouter: Error processing gui.action.Start@1b7c473a java.lang.ArrayIndexOutOfBoundsException

    一个使用了很久的Jmeter脚本,运行时Jmeter的UI界面上点击绿色按钮后,完全无反应,只有log报错,如下: 2017/06/28 14:29:23 ERROR - jmeter.gui.act ...

  4. logstash-3-输出到es中

    之前测试 filebeat和logstash的时候, 使用的是stdout标准输出, 现在我们想把数据输出到es中去, 1, 首先需要一个es: 修改配置文件 后台启动 ./bin/elasticse ...

  5. linux中为什么cpu使用率会超过100见解

    linux的cpu使用频率是根据cpu个数和核数决定的 top,然后你按一下键盘的1,这就是单个核心的负载,不然是所有核心的负载相加,自然会超过100 如上面 cpu个数是4个,那么cpu可以占到40 ...

  6. Eclipse 下Spring cloud项目集成开发插件Spring Tool Suite (STS) 安装

    安装eclipse插件 Help->Eclipse Marketplace-> Search中查找"Spring Tool Suite (STS) for Eclipse&quo ...

  7. C#数据仓储类

    https://ninesky.codeplex.com/SourceControl/latest /*============================== 版本:v0.1 创建:2016.2 ...

  8. SQL Server 2008 安装重启电脑失败

    a .重启机器,再进行安装,如果发现还有该错误,请按下面步骤b.在开始->运行中输入regeditc.到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\C ...

  9. 数据存储之属性列表Plist

    常用的数据存储有属性列表.偏好设置.归档.sqlite.coreData.上一博客了解了沙盒,现在了解下属性列表Plist. 通常通过NSArray.NSDictionary集合类的WriteToFi ...

  10. SpringBoot+SpringData 整合入门

    SpringData概述 SpringData :Spring的一个子项目.用于简化数据库访问,支持NoSQL和关系数据存储.其主要目标是使用数据库的访问变得方便快捷. SpringData 项目所支 ...