转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39269193,本文出自:【张鸿洋的博客】

1、概述

首先我们来吹吹牛,什么叫IoC,控制反转(Inversion of Control,英文缩写为IoC),什么意思呢?

就是你一个类里面需要用到很多个成员变量,传统的写法,你要用这些成员变量,那么你就new 出来用呗~~

IoC的原则是:NO,我们不要new,这样耦合度太高;你配置个xml文件,里面标明哪个类,里面用了哪些成员变量,等待加载这个类的时候,我帮你注入(new)进去;

这样做有什么好处呢?

回答这个问题,刚好可以回答另一个问题,很多人问,项目分层开发是吧,分为控制层、业务层、DAO层神马的。然后每一层为撒子要一个包放接口,一个包放实现呢?只要一个实现包不行么~刚好,如果你了解了IoC,你就知道这些个接口的作用了,上面不是说,你不用new,你只要声明了成员变量+写个配置文件,有人帮你new;此时,你在类中,就可以把需要使用到的成员变量都声明成接口,然后你会发现,当实现类发生变化的时候,或者切换实现类,你需要做什么呢?你只要在配置文件里面做个简单的修改。如果你用的就是实实在在的实现类,现在换实现类,你需要找到所有声明这个实现类的地方,手动修改类名;如果你遇到了一个多变的老大,是吧,呵呵~

当然了,很多会觉得,写个配置文件,卧槽,这多麻烦。于是乎,又出现了另一种方案,得,你闲配置文件麻烦,你用注解吧。你在需要注入的成员变量上面给我加个注解,例如:@Inject,这样就行了,你总不能说这么个单词麻烦吧~~

当然了,有了配置文件和注解,那么怎么注入呢?其实就是把字符串类路径变成类么,当然了,反射上场了;话说,很久很久以前,反射很慢啊,嗯,那是很久很久以前,现在已经不是太慢了,当然了肯定达不到原生的速度~~无反射,没有任何框架。

如果你觉得注解,反射神马的好高级。我说一句:Just Do It ,你会发现注解就和你写一个普通JavaBean差不多;反射呢?API就那么几行,千万不要被震慑住~

2、框架实现

得进入正题了,Android IOC框架,其实主要就是帮大家注入所有的控件,布局文件什么的。如果你用过xUtils,afinal类的框架,你肯定不陌生~

注入View

假设:我们一个Activity,里面10来个View。

传统做法:我们需要先给这个Activity设置下布局文件,然后在onCreate里面一个一个的findViewById把~

目标的做法:Activity类上添加个注解,帮我们自动注入布局文科;声明View的时候,添加一行注解,然后自动帮我们findViewById;

于是乎我们的目标类是这样的:

[java] view plaincopy
  1. @ContentView(value = R.layout.activity_main)
  2. public class MainActivity extends BaseActivity
  3. {
  4. @ViewInject(R.id.id_btn)
  5. private Button mBtn1;
  6. @ViewInject(R.id.id_btn02)
  7. private Button mBtn2;

3、编码

1、定义注解

首先我们需要两个注解文件:

[java] view plaincopy
  1. package com.zhy.ioc.view.annotation;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target(ElementType.TYPE)
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface ContentView
  9. {
  10. int value();
  11. }

ContentView用于在类上使用,主要用于标明该Activity需要使用的布局文件。

[java] view plaincopy
  1. @ContentView(value = R.layout.activity_main)
  2. public class MainActivity
[java] view plaincopy
  1. package com.zhy.ioc.view.annotation;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target(ElementType.FIELD)
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface ViewInject
  9. {
  10. int value();
  11. }

在成员变量上使用,用于指定View的Id

[java] view plaincopy
  1. @ViewInject(R.id.id_btn)
  2. private Button mBtn1;

简单说一下注解:定义的关键字@interface ; @Target表示该注解可以用于什么地方,可能的类型TYPE(类),FIELD(成员变量),可能的类型:

[java] view plaincopy
  1. public enum ElementType {
  2. /**
  3. * Class, interface or enum declaration.
  4. */
  5. TYPE,
  6. /**
  7. * Field declaration.
  8. */
  9. FIELD,
  10. /**
  11. * Method declaration.
  12. */
  13. METHOD,
  14. /**
  15. * Parameter declaration.
  16. */
  17. PARAMETER,
  18. /**
  19. * Constructor declaration.
  20. */
  21. CONSTRUCTOR,
  22. /**
  23. * Local variable declaration.
  24. */
  25. LOCAL_VARIABLE,
  26. /**
  27. * Annotation type declaration.
  28. */
  29. ANNOTATION_TYPE,
  30. /**
  31. * Package declaration.
  32. */
  33. PACKAGE
  34. }

就是这些个枚举。

@Retention表示:表示需要在什么级别保存该注解信息;我们这里设置为运行时。

可能的类型:

[java] view plaincopy
  1. public enum RetentionPolicy {
  2. /**
  3. * Annotation is only available in the source code.
  4. */
  5. SOURCE,
  6. /**
  7. * Annotation is available in the source code and in the class file, but not
  8. * at runtime. This is the default policy.
  9. */
  10. CLASS,
  11. /**
  12. * Annotation is available in the source code, the class file and is
  13. * available at runtime.
  14. */
  15. RUNTIME
  16. }

这些个枚举~

2、MainActivity

[java] view plaincopy
  1. package com.zhy.zhy_xutils_test;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.view.View;
  5. import android.view.View.OnClickListener;
  6. import android.widget.Button;
  7. import android.widget.Toast;
  8. import com.zhy.ioc.view.ViewInjectUtils;
  9. import com.zhy.ioc.view.annotation.ContentView;
  10. import com.zhy.ioc.view.annotation.ViewInject;
  11. @ContentView(value = R.layout.activity_main)
  12. public class MainActivity extends Activity implements OnClickListener
  13. {
  14. @ViewInject(R.id.id_btn)
  15. private Button mBtn1;
  16. @ViewInject(R.id.id_btn02)
  17. private Button mBtn2;
  18. @Override
  19. protected void onCreate(Bundle savedInstanceState)
  20. {
  21. super.onCreate(savedInstanceState);
  22. ViewInjectUtils.inject(this);
  23. mBtn1.setOnClickListener(this);
  24. mBtn2.setOnClickListener(this);
  25. }
  26. @Override
  27. public void onClick(View v)
  28. {
  29. switch (v.getId())
  30. {
  31. case R.id.id_btn:
  32. Toast.makeText(MainActivity.this, "Why do you click me ?",
  33. Toast.LENGTH_SHORT).show();
  34. break;
  35. case R.id.id_btn02:
  36. Toast.makeText(MainActivity.this, "I am sleeping !!!",
  37. Toast.LENGTH_SHORT).show();
  38. break;
  39. }
  40. }
  41. }

注解都写好了,核心的代码就是ViewInjectUtils.inject(this)了~

3、ViewInjectUtils

1、首先是注入主布局文件的代码:

[java] view plaincopy
  1. /**
  2. * 注入主布局文件
  3. *
  4. * @param activity
  5. */
  6. private static void injectContentView(Activity activity)
  7. {
  8. Class<? extends Activity> clazz = activity.getClass();
  9. // 查询类上是否存在ContentView注解
  10. ContentView contentView = clazz.getAnnotation(ContentView.class);
  11. if (contentView != null)// 存在
  12. {
  13. int contentViewLayoutId = contentView.value();
  14. try
  15. {
  16. Method method = clazz.getMethod(METHOD_SET_CONTENTVIEW,
  17. int.class);
  18. method.setAccessible(true);
  19. method.invoke(activity, contentViewLayoutId);
  20. } catch (Exception e)
  21. {
  22. e.printStackTrace();
  23. }
  24. }
  25. }

通过传入的activity对象,获得它的Class类型,判断是否写了ContentView这个注解,如果写了,读取它的value,然后得到setContentView这个方法,使用invoke进行调用;

有个常量:

[java] view plaincopy
  1. private static final String METHOD_SET_CONTENTVIEW = "setContentView";

2、接下来是注入Views

[java] view plaincopy
  1. private static final String METHOD_FIND_VIEW_BY_ID = "findViewById";
  2. /**
  3. * 注入所有的控件
  4. *
  5. * @param activity
  6. */
  7. private static void injectViews(Activity activity)
  8. {
  9. Class<? extends Activity> clazz = activity.getClass();
  10. Field[] fields = clazz.getDeclaredFields();
  11. // 遍历所有成员变量
  12. for (Field field : fields)
  13. {
  14. ViewInject viewInjectAnnotation = field
  15. .getAnnotation(ViewInject.class);
  16. if (viewInjectAnnotation != null)
  17. {
  18. int viewId = viewInjectAnnotation.value();
  19. if (viewId != -1)
  20. {
  21. Log.e("TAG", viewId+"");
  22. // 初始化View
  23. try
  24. {
  25. Method method = clazz.getMethod(METHOD_FIND_VIEW_BY_ID,
  26. int.class);
  27. Object resView = method.invoke(activity, viewId);
  28. field.setAccessible(true);
  29. field.set(activity, resView);
  30. } catch (Exception e)
  31. {
  32. e.printStackTrace();
  33. }
  34. }
  35. }
  36. }
  37. }

获取声明的所有的属性,遍历,找到存在ViewInject注解的属性,或者其value,然后去调用findViewById方法,最后把值设置给field~~~

好了,把这两个方法写到inject里面就好了。

[java] view plaincopy
  1. public static void inject(Activity activity)
  2. {
  3. injectContentView(activity);
  4. injectViews(activity);
  5. }

本文主要了解了如何打造这么个框架,下一篇,将教大家如何注入事件 ,不要再写什么setXXXListener了~~~

效果图:

源码点击下载

Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (上)相关推荐

  1. Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (下)

    上一篇博客我们已经带大家简单的吹了一下IoC,实现了Activity中View的布局以及控件的注入,如果你不了解,请参考:Android 进阶 教你打造 Android 中的 IOC 框架 [View ...

  2. 根据用户查进程_【磨叽教程】Android进阶教程之在Android系统下各进程之间的优先级关系...

    导读:本文大约2000字,预计阅读时间3分钟.本文纯属技术文,无推广. 正文     首先应用进程的生命周期并不由应用本身直接控制,而是由系统综合多种因素来确定的.Android系统有自己的一套标准, ...

  3. Android:手把手教你打造可缩放移动的ImageView(下)

    在上一篇Android:手把手教你打造可缩放移动的ImageView最后提出了一个注意点:当自定义的MatrixImageView如ViewPager.ListView等带有滑动效果的ViewGrou ...

  4. Android进阶之路 - 软键盘中右下角的设置与监听

    在项目中,多多少少会遇到修改软键盘右下角按钮的需求,虽然已经写过几次,但是还是觉得在这里专心做个笔记比较放心 ~ 我的那些软键盘Blog ~ Android进阶之路 - 常见软键盘操作行为 Andro ...

  5. 【我的Android进阶之旅】Android 混淆文件资源分类整理之二:将混淆文件拆分成更小粒度的混淆文件

    在我2017年的文章[我的Android进阶之旅]Android 混淆文件资源分类整理中,我已经提及过. 之前将所有的混淆都配置在一个 proguard-rules.pro 这个Android Stu ...

  6. Android 进阶——Framework 核心之Android Storage Access Framework(SAF)存储访问框架机制详解(一)

    文章大纲 引言 一.Android Storage Access Framework 二.Storage Access Framework 的主要角色成员 1.Document Provider 文件 ...

  7. Android 进阶——Framework 核心之Android Storage Access Framework(SAF)存储访问框架机制详解(二)

    文章大纲 引言 一.DirectFragment 1.当选中DirectoryFragment中RecyclerView的Item时 2.选中DirectoryFragment中RecyclerVie ...

  8. 【我的Android进阶之旅】Android混淆踩坑之各模块各自单独配置混淆,但是将minifyEnabled设置为true导致的编译错误

    一.背景描述 在之前的两篇文章中 [我的Android进阶之旅]Android 混淆文件资源分类整理 [我的Android进阶之旅]Android 混淆文件资源分类整理之二:将混淆文件拆分成更小粒度的 ...

  9. 【我的Android进阶之旅】Android使用JNI的时候报native crash: A/libc: Fatal signal 4 (SIGILL), code 2 (ILL_ILLOPN)

    一.问题描述 最近在JNI开发中,[我的Android进阶之旅]Android 如何防止 so库文件被未知应用盗用? 抛了一个异常,然后运行的时候报如下所示的错误: 2021-01-08 14:25: ...

最新文章

  1. List再整理,从代码底层全面解析List(看完后保证收获满满)
  2. select into mysql_MySQL select into 和 SQL select into
  3. 广东海洋大学体育馆管理系统 开发日记2
  4. [转载]织梦后台添加新变量-内容模型
  5. try catch finally的用法
  6. Java程序停止 mq通道未关闭_java – 如何在MQ上停止丢失消息
  7. 算法设计与分析期末复习题
  8. 存储器容量大小和芯片选择
  9. 龙之谷手游服务器修改,龙之谷手游互通区一览 5月12日部分服务器数据互通公告...
  10. android 如何进入安全模式,手机怎么进入安全模式
  11. Javaweb鼠标事件案例分析—鼠标移入移出表格颜色变化,kafka面试题
  12. 应用之星破除行业门槛 零成本开发手机应用
  13. D-OJ刷题日记:一元多项式的运算 题目编号:463
  14. C++设计模式 | 四种创建型模式——简单工厂模式、工厂方法模式、抽象工厂模式、单例模式...
  15. 计算机网络布线教学,计算机网络综合布线【基于项目的《网络综合布线》课程理实一体化教学思考】...
  16. 气相预报用计算机的,煤自燃火灾指标气体预测预报的几个关键问题探讨
  17. 英特尔 x 趋视科技,理想社区从这里启航
  18. 如何将INS惯导经、纬、高数据在谷歌地球上绘制轨迹
  19. 【Python】编写代码对网络上的ip地址进行端口扫描,收集“ip+开放端口”信息
  20. mysql.yog基本用法,mysql日常使用注意事项(基础篇)

热门文章

  1. java jvm容器_java – YARN:容器和JVM
  2. Ubuntu系统---C++之Eclipse 开始工程项目
  3. 分块入门(根据hzwer的博客。。)(右端点是r不是n。。)
  4. 可访问范围 与 visual 或 abstract “修饰符”
  5. BZOJ 1101: [POI2007]Zap( 莫比乌斯反演 )
  6. hdu4513--Manacher算法--回文串的O(n)算法
  7. 动态T-SQL语句常見問題與解決方案
  8. CPU主频频率越高,手机运行速度就越快吗?
  9. Kotlin 基础语法
  10. Android Studio-AndroidStudio目录结构