自己来实现一套IOC注解框架
这个时候有人就开始喷了,明知道反射会影响性能为什么还要用?这里我就随便说说吧,我承认反射会影响性能但是问题不大我们可以自己去测试反射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注解框架的更多相关文章
- Android面试基础(一)IOC(DI)框架(ViewUtils)讲解_反射和自定义注解类
1. Android中的IOC(DI)框架 1.1 ViewUtils简介(xUtils中的四大部分之一) IOC: Inverse of Controller 控制反转. DI: Dependenc ...
- Spring框架学习(6)使用ioc注解方式配置bean
内容源自:使用ioc注解方式配置bean context层 : 上下文环境/容器环境 applicationContext.xml 1 ioc注解功能 注解 简化xml文件配置 如 hibernate ...
- 仿写一个简陋的 IOC/AOP 框架 mini-spring
讲道理,感觉自己有点菜.Spring 源码看不懂,不想强行解释,等多积累些项目经验之后再看吧,但是 Spring 中的控制反转(IOC)和面向切面编程(AOP)思想很重要,为了更好的使用 Spring ...
- java注解框架
我们经常会在java代码里面看到:“@Override”,“@Target”等等样子的东西,这些是什么? 在java里面它们是“注解”. 下面是百度百科的解释:java.lang.annotation ...
- 注解框架---AndroidAnnotations
AndroidAnnotations是一个开源框架,旨在加快Android开发的效率.通过使用它开放出来的注解api,你差点儿可以使用在不论什么地方, 大大的降低了无关痛痒的代码量,让开发人员可以抽身 ...
- Android注解框架实战-ButterKnife
文章大纲 Android注解框架介绍 ButterKnife实战 项目源码下载 一.框架介绍 为什么要用注解框架? 在Android开发过程中,我们经常性地需要操作组件,操作方法有findVie ...
- Android 注解框架对比
Java的注解(Annotation)相当于一种标记,在程序中加入注解就等于为程序打上某种标记,标记可以加在包,类,属性,方法,本地变量上.然后你可以写一个注解处理器去解析处理这些注解(人称编译时注解 ...
- ButterKnife注解框架详解
Android 懒人注解框架 :https://github.com/JakeWharton/butterknife 前言: 注解,相信很多同学都用到了,对控件进行初始化的时候需要用到 findVie ...
- Spring的IOC注解开发入门1
基本知识点如下: 引入注解约束,配置组件扫描 类上的注解: @Conponent @Controller @Service @Repository 普通属性的注解 @value 对象属性的注解 ...
随机推荐
- (转) 面向对象设计原则(二):开放-封闭原则(OCP)
原文:https://blog.csdn.net/tjiyu/article/details/57079927 面向对象设计原则(二):开放-封闭原则(OCP) 开放-封闭原则(Open-closed ...
- c++中的复合类型
复合类型是指基于其他类型而定义的类型. 而这里介绍的是引用和指针.并且指针和引用都提供了对其他对象的间接访问. 引用 引用还是很好理解的,就是为对象起了另外一个名字,引用类型引用另外一种类型. 通常将 ...
- Android四大组件之---activity生命周期详解
废话不多说, 先来一张Google提供的一张经典的生命周期流程图: 有的朋友可能看英文的有点费劲,再提供一张中文版的 O(∩_∩)O 相信已经很多人对这张图再熟悉不过了,下面笔者按照自己的理解并结合 ...
- 全网最详细的用pip安装****模块报错:Could not find a version that satisfies the requirement ****(from version:) No matching distribution found for ****的解决办法(图文详解)
不多说,直接上干货! 问题详情 这个问题,很普遍.如我这里想实现,Windows下Anaconda2 / Anaconda3里正确下载安装用来向微信好友发送消息的itchat库. 见,我撰写的 全网最 ...
- 全网最详细的Sublime Text 3的激活(图文详解)
不多说,直接上干货! 前期博客 全网最详细的Windows里下载与安装Sublime Text *(图文详解) ZYNGA INC. User License EA7E- 927BA117 84C93 ...
- JavaScript -- Window-Focus
-----034-Window-Focus.html----- <!DOCTYPE html> <html> <head> <meta http-equiv= ...
- 记一次解决CSS定位bug思路
事因 网站中的遮罩层大都有一个问题,就是在这个遮罩层中滑动,里面的内容也会跟着滑动,我是这样想的,既然都有这个问题,干脆写一个通用的插件出来,省的每个还得单独处理.如果是单独处理这个问题是比较好解决的 ...
- Microsoft.Net 版本
Date Framework Visual Studio C# CLR 2002.2 1.0 Visual Studio 2002 1.0 1.0 2003.4 1.1 Visual Studio 2 ...
- window如何一键关闭所有进程程序
1.桌面创建一个快捷方式,点击鼠标右键跳出的菜单=> 新建=>快捷方式 2. 在“请键入对象的位置(T)”下方的文本框中输入: taskkill /F /FI "USERNAME ...
- 查询Sql Server数据库对象结构
查询Sql Server数据库对象结构 查询数据库 查询架构 查询表 查询列 查询存储过程 查询视图 1.查询某一服务器下所有数据库 select t.[name] as 数据库 from sys.d ...